2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
100 * @cfg {String} offset
101 * The number of pixels to offset the shadow from the element (defaults to 4)
109 * Displays the shadow under the target element
110 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
112 show : function(target){
113 target = Roo.get(target);
115 this.el = Roo.Shadow.Pool.pull();
116 if(this.el.dom.nextSibling != target.dom){
117 this.el.insertBefore(target);
120 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
122 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
125 target.getLeft(true),
130 this.el.dom.style.display = "block";
134 * Returns true if the shadow is visible, else false
136 isVisible : function(){
137 return this.el ? true : false;
141 * Direct alignment when values are already available. Show must be called at least once before
142 * calling this method to ensure it is initialized.
143 * @param {Number} left The target element left position
144 * @param {Number} top The target element top position
145 * @param {Number} width The target element width
146 * @param {Number} height The target element height
148 realign : function(l, t, w, h){
152 var a = this.adjusts, d = this.el.dom, s = d.style;
154 s.left = (l+a.l)+"px";
155 s.top = (t+a.t)+"px";
156 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
158 if(s.width != sws || s.height != shs){
162 var cn = d.childNodes;
163 var sww = Math.max(0, (sw-12))+"px";
164 cn[0].childNodes[1].style.width = sww;
165 cn[1].childNodes[1].style.width = sww;
166 cn[2].childNodes[1].style.width = sww;
167 cn[1].style.height = Math.max(0, (sh-12))+"px";
177 this.el.dom.style.display = "none";
178 Roo.Shadow.Pool.push(this.el);
184 * Adjust the z-index of this shadow
185 * @param {Number} zindex The new z-index
187 setZIndex : function(z){
190 this.el.setStyle("z-index", z);
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
198 var markup = Roo.isIE ?
199 '<div class="x-ie-shadow"></div>' :
200 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
205 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206 sh.autoBoxAdjust = false;
218 * base class for bootstrap elements.
222 Roo.bootstrap = Roo.bootstrap || {};
224 * @class Roo.bootstrap.Component
225 * @extends Roo.Component
226 * Bootstrap Component base class
227 * @cfg {String} cls css class
228 * @cfg {String} style any extra css
229 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
231 * @cfg {string} dataId cutomer id
232 * @cfg {string} name Specifies name attribute
233 * @cfg {string} tooltip Text for the tooltip
234 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
235 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
238 * Do not use directly - it does not do anything..
239 * @param {Object} config The config object
244 Roo.bootstrap.Component = function(config){
245 Roo.bootstrap.Component.superclass.constructor.call(this, config);
249 * @event childrenrendered
250 * Fires when the children have been rendered..
251 * @param {Roo.bootstrap.Component} this
253 "childrenrendered" : true
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
265 allowDomMove : false, // to stop relocations in parent onRender...
275 * Initialize Events for the element
277 initEvents : function() { },
283 can_build_overlaid : true,
285 container_method : false,
292 // returns the parent component..
293 return Roo.ComponentMgr.get(this.parentId)
299 onRender : function(ct, position)
301 // Roo.log("Call onRender: " + this.xtype);
303 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
306 if (this.el.attr('xtype')) {
307 this.el.attr('xtypex', this.el.attr('xtype'));
308 this.el.dom.removeAttribute('xtype');
318 var cfg = Roo.apply({}, this.getAutoCreate());
320 cfg.id = this.id || Roo.id();
322 // fill in the extra attributes
323 if (this.xattr && typeof(this.xattr) =='object') {
324 for (var i in this.xattr) {
325 cfg[i] = this.xattr[i];
330 cfg.dataId = this.dataId;
334 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
337 if (this.style) { // fixme needs to support more complex style data.
338 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
342 cfg.name = this.name;
345 this.el = ct.createChild(cfg, position);
348 this.tooltipEl().attr('tooltip', this.tooltip);
351 if(this.tabIndex !== undefined){
352 this.el.dom.setAttribute('tabIndex', this.tabIndex);
359 * Fetch the element to add children to
360 * @return {Roo.Element} defaults to this.el
362 getChildContainer : function()
366 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
368 return Roo.get(document.body);
372 * Fetch the element to display the tooltip on.
373 * @return {Roo.Element} defaults to this.el
375 tooltipEl : function()
380 addxtype : function(tree,cntr)
384 cn = Roo.factory(tree);
385 //Roo.log(['addxtype', cn]);
387 cn.parentType = this.xtype; //??
388 cn.parentId = this.id;
390 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391 if (typeof(cn.container_method) == 'string') {
392 cntr = cn.container_method;
396 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
398 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
400 var build_from_html = Roo.XComponent.build_from_html;
402 var is_body = (tree.xtype == 'Body') ;
404 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
406 var self_cntr_el = Roo.get(this[cntr](false));
408 // do not try and build conditional elements
409 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
413 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415 return this.addxtypeChild(tree,cntr, is_body);
418 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
421 return this.addxtypeChild(Roo.apply({}, tree),cntr);
424 Roo.log('skipping render');
430 if (!build_from_html) {
434 // this i think handles overlaying multiple children of the same type
435 // with the sam eelement.. - which might be buggy..
437 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
447 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
454 addxtypeChild : function (tree, cntr, is_body)
456 Roo.debug && Roo.log('addxtypeChild:' + cntr);
458 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
461 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462 (typeof(tree['flexy:foreach']) != 'undefined');
466 skip_children = false;
467 // render the element if it's not BODY.
470 // if parent was disabled, then do not try and create the children..
471 if(!this[cntr](true)){
476 cn = Roo.factory(tree);
478 cn.parentType = this.xtype; //??
479 cn.parentId = this.id;
481 var build_from_html = Roo.XComponent.build_from_html;
484 // does the container contain child eleemnts with 'xtype' attributes.
485 // that match this xtype..
486 // note - when we render we create these as well..
487 // so we should check to see if body has xtype set.
488 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
490 var self_cntr_el = Roo.get(this[cntr](false));
491 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
493 //Roo.log(Roo.XComponent.build_from_html);
494 //Roo.log("got echild:");
497 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498 // and are not displayed -this causes this to use up the wrong element when matching.
499 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
502 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509 //echild.dom.removeAttribute('xtype');
511 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512 Roo.debug && Roo.log(self_cntr_el);
513 Roo.debug && Roo.log(echild);
514 Roo.debug && Roo.log(cn);
520 // if object has flexy:if - then it may or may not be rendered.
521 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
522 // skip a flexy if element.
523 Roo.debug && Roo.log('skipping render');
524 Roo.debug && Roo.log(tree);
526 Roo.debug && Roo.log('skipping all children');
527 skip_children = true;
532 // actually if flexy:foreach is found, we really want to create
533 // multiple copies here...
535 //Roo.log(this[cntr]());
536 // some elements do not have render methods.. like the layouts...
538 if(this[cntr](true) === false){
543 cn.render && cn.render(this[cntr](true));
546 // then add the element..
553 if (typeof (tree.menu) != 'undefined') {
554 tree.menu.parentType = cn.xtype;
555 tree.menu.triggerEl = cn.el;
556 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
560 if (!tree.items || !tree.items.length) {
562 //Roo.log(["no children", this]);
567 var items = tree.items;
570 //Roo.log(items.length);
572 if (!skip_children) {
573 for(var i =0;i < items.length;i++) {
574 // Roo.log(['add child', items[i]]);
575 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581 //Roo.log("fire childrenrendered");
583 cn.fireEvent('childrenrendered', this);
589 * Set the element that will be used to show or hide
591 setVisibilityEl : function(el)
593 this.visibilityEl = el;
597 * Get the element that will be used to show or hide
599 getVisibilityEl : function()
601 if (typeof(this.visibilityEl) == 'object') {
602 return this.visibilityEl;
605 if (typeof(this.visibilityEl) == 'string') {
606 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
613 * Show a component - removes 'hidden' class
617 if(!this.getVisibilityEl()){
621 this.getVisibilityEl().removeClass(['hidden','d-none']);
623 this.fireEvent('show', this);
628 * Hide a component - adds 'hidden' class
632 if(!this.getVisibilityEl()){
636 this.getVisibilityEl().addClass(['hidden','d-none']);
638 this.fireEvent('hide', this);
651 * @class Roo.bootstrap.Element
652 * @extends Roo.bootstrap.Component
653 * Bootstrap Element class
654 * @cfg {String} html contents of the element
655 * @cfg {String} tag tag of the element
656 * @cfg {String} cls class of the element
657 * @cfg {Boolean} preventDefault (true|false) default false
658 * @cfg {Boolean} clickable (true|false) default false
659 * @cfg {String} role default blank - set to button to force cursor pointer
663 * Create a new Element
664 * @param {Object} config The config object
667 Roo.bootstrap.Element = function(config){
668 Roo.bootstrap.Element.superclass.constructor.call(this, config);
674 * When a element is chick
675 * @param {Roo.bootstrap.Element} this
676 * @param {Roo.EventObject} e
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
689 preventDefault: false,
694 getAutoCreate : function(){
698 // cls: this.cls, double assign in parent class Component.js :: onRender
701 if (this.role !== false) {
702 cfg.role = this.role;
708 initEvents: function()
710 Roo.bootstrap.Element.superclass.initEvents.call(this);
713 this.el.on('click', this.onClick, this);
719 onClick : function(e)
721 if(this.preventDefault){
725 this.fireEvent('click', this, e); // why was this double click before?
733 getValue : function()
735 return this.el.dom.innerHTML;
738 setValue : function(value)
740 this.el.dom.innerHTML = value;
755 * @class Roo.bootstrap.DropTarget
756 * @extends Roo.bootstrap.Element
757 * Bootstrap DropTarget class
759 * @cfg {string} name dropable name
762 * Create a new Dropable Area
763 * @param {Object} config The config object
766 Roo.bootstrap.DropTarget = function(config){
767 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
773 * When a element is chick
774 * @param {Roo.bootstrap.Element} this
775 * @param {Roo.EventObject} e
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
784 getAutoCreate : function(){
789 initEvents: function()
791 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
795 drop : this.dragDrop.createDelegate(this),
796 enter : this.dragEnter.createDelegate(this),
797 out : this.dragOut.createDelegate(this),
798 over : this.dragOver.createDelegate(this)
802 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
805 dragDrop : function(source,e,data)
807 // user has to decide how to impliment this.
810 //this.fireEvent('drop', this, source, e ,data);
814 dragEnter : function(n, dd, e, data)
816 // probably want to resize the element to match the dropped element..
818 this.originalSize = this.el.getSize();
819 this.el.setSize( n.el.getSize());
820 this.dropZone.DDM.refreshCache(this.name);
821 Roo.log([n, dd, e, data]);
824 dragOut : function(value)
826 // resize back to normal
828 this.el.setSize(this.originalSize);
829 this.dropZone.resetConstraints();
832 dragOver : function()
849 * @class Roo.bootstrap.Body
850 * @extends Roo.bootstrap.Component
851 * Bootstrap Body class
855 * @param {Object} config The config object
858 Roo.bootstrap.Body = function(config){
860 config = config || {};
862 Roo.bootstrap.Body.superclass.constructor.call(this, config);
863 this.el = Roo.get(config.el ? config.el : document.body );
864 if (this.cls && this.cls.length) {
865 Roo.get(document.body).addClass(this.cls);
869 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
871 is_body : true,// just to make sure it's constructed?
876 onRender : function(ct, position)
878 /* Roo.log("Roo.bootstrap.Body - onRender");
879 if (this.cls && this.cls.length) {
880 Roo.get(document.body).addClass(this.cls);
899 * @class Roo.bootstrap.ButtonGroup
900 * @extends Roo.bootstrap.Component
901 * Bootstrap ButtonGroup class
902 * @cfg {String} size lg | sm | xs (default empty normal)
903 * @cfg {String} align vertical | justified (default none)
904 * @cfg {String} direction up | down (default down)
905 * @cfg {Boolean} toolbar false | true
906 * @cfg {Boolean} btn true | false
911 * @param {Object} config The config object
914 Roo.bootstrap.ButtonGroup = function(config){
915 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
918 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
926 getAutoCreate : function(){
932 cfg.html = this.html || cfg.html;
943 if (['vertical','justified'].indexOf(this.align)!==-1) {
944 cfg.cls = 'btn-group-' + this.align;
946 if (this.align == 'justified') {
947 console.log(this.items);
951 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
952 cfg.cls += ' btn-group-' + this.size;
955 if (this.direction == 'up') {
956 cfg.cls += ' dropup' ;
962 * Add a button to the group (similar to NavItem API.)
964 addItem : function(cfg)
966 var cn = new Roo.bootstrap.Button(cfg);
968 cn.parentId = this.id;
969 cn.onRender(this.el, null);
983 * @class Roo.bootstrap.Button
984 * @extends Roo.bootstrap.Component
985 * Bootstrap Button class
986 * @cfg {String} html The button content
987 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
988 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
989 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
990 * @cfg {String} size (lg|sm|xs)
991 * @cfg {String} tag (a|input|submit)
992 * @cfg {String} href empty or href
993 * @cfg {Boolean} disabled default false;
994 * @cfg {Boolean} isClose default false;
995 * @cfg {String} glyphicon depricated - use fa
996 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
997 * @cfg {String} badge text for badge
998 * @cfg {String} theme (default|glow)
999 * @cfg {Boolean} inverse dark themed version
1000 * @cfg {Boolean} toggle is it a slidy toggle button
1001 * @cfg {Boolean} pressed default null - if the button ahs active state
1002 * @cfg {String} ontext text for on slidy toggle state
1003 * @cfg {String} offtext text for off slidy toggle state
1004 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1005 * @cfg {Boolean} removeClass remove the standard class..
1006 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1007 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1010 * Create a new button
1011 * @param {Object} config The config object
1015 Roo.bootstrap.Button = function(config){
1016 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1022 * When a button is pressed
1023 * @param {Roo.bootstrap.Button} btn
1024 * @param {Roo.EventObject} e
1029 * When a button is double clicked
1030 * @param {Roo.bootstrap.Button} btn
1031 * @param {Roo.EventObject} e
1036 * After the button has been toggles
1037 * @param {Roo.bootstrap.Button} btn
1038 * @param {Roo.EventObject} e
1039 * @param {boolean} pressed (also available as button.pressed)
1045 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1066 preventDefault: true,
1075 getAutoCreate : function(){
1083 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1084 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1085 this.tag = 'button';
1089 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1091 if (this.toggle == true) {
1094 cls: 'slider-frame roo-button',
1098 'data-on-text':'ON',
1099 'data-off-text':'OFF',
1100 cls: 'slider-button',
1105 // why are we validating the weights?
1106 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1107 cfg.cls += ' ' + this.weight;
1114 cfg.cls += ' close';
1116 cfg["aria-hidden"] = true;
1118 cfg.html = "×";
1124 if (this.theme==='default') {
1125 cfg.cls = 'btn roo-button';
1127 //if (this.parentType != 'Navbar') {
1128 this.weight = this.weight.length ? this.weight : 'default';
1130 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1132 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1133 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1134 cfg.cls += ' btn-' + outline + weight;
1135 if (this.weight == 'default') {
1137 cfg.cls += ' btn-' + this.weight;
1140 } else if (this.theme==='glow') {
1143 cfg.cls = 'btn-glow roo-button';
1145 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1147 cfg.cls += ' ' + this.weight;
1153 this.cls += ' inverse';
1157 if (this.active || this.pressed === true) {
1158 cfg.cls += ' active';
1161 if (this.disabled) {
1162 cfg.disabled = 'disabled';
1166 Roo.log('changing to ul' );
1168 this.glyphicon = 'caret';
1169 if (Roo.bootstrap.version == 4) {
1170 this.fa = 'caret-down';
1175 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1177 //gsRoo.log(this.parentType);
1178 if (this.parentType === 'Navbar' && !this.parent().bar) {
1179 Roo.log('changing to li?');
1188 href : this.href || '#'
1191 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1192 cfg.cls += ' dropdown';
1199 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1201 if (this.glyphicon) {
1202 cfg.html = ' ' + cfg.html;
1207 cls: 'glyphicon glyphicon-' + this.glyphicon
1212 cfg.html = ' ' + cfg.html;
1217 cls: 'fa fas fa-' + this.fa
1227 // cfg.cls='btn roo-button';
1231 var value = cfg.html;
1236 cls: 'glyphicon glyphicon-' + this.glyphicon,
1243 cls: 'fa fas fa-' + this.fa,
1248 var bw = this.badge_weight.length ? this.badge_weight :
1249 (this.weight.length ? this.weight : 'secondary');
1250 bw = bw == 'default' ? 'secondary' : bw;
1256 cls: 'badge badge-' + bw,
1265 cfg.cls += ' dropdown';
1266 cfg.html = typeof(cfg.html) != 'undefined' ?
1267 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1270 if (cfg.tag !== 'a' && this.href !== '') {
1271 throw "Tag must be a to set href.";
1272 } else if (this.href.length > 0) {
1273 cfg.href = this.href;
1276 if(this.removeClass){
1281 cfg.target = this.target;
1286 initEvents: function() {
1287 // Roo.log('init events?');
1288 // Roo.log(this.el.dom);
1291 if (typeof (this.menu) != 'undefined') {
1292 this.menu.parentType = this.xtype;
1293 this.menu.triggerEl = this.el;
1294 this.addxtype(Roo.apply({}, this.menu));
1298 if (this.el.hasClass('roo-button')) {
1299 this.el.on('click', this.onClick, this);
1300 this.el.on('dblclick', this.onDblClick, this);
1302 this.el.select('.roo-button').on('click', this.onClick, this);
1303 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1307 if(this.removeClass){
1308 this.el.on('click', this.onClick, this);
1311 if (this.group === true) {
1312 if (this.pressed === false || this.pressed === true) {
1315 this.pressed = false;
1316 this.setActive(this.pressed);
1321 this.el.enableDisplayMode();
1324 onClick : function(e)
1326 if (this.disabled) {
1330 Roo.log('button on click ');
1331 if(this.preventDefault){
1340 this.setActive(true);
1341 var pi = this.parent().items;
1342 for (var i = 0;i < pi.length;i++) {
1343 if (this == pi[i]) {
1346 if (pi[i].el.hasClass('roo-button')) {
1347 pi[i].setActive(false);
1350 this.fireEvent('click', this, e);
1354 if (this.pressed === true || this.pressed === false) {
1355 this.toggleActive(e);
1359 this.fireEvent('click', this, e);
1361 onDblClick: function(e)
1363 if (this.disabled) {
1366 if(this.preventDefault){
1369 this.fireEvent('dblclick', this, e);
1372 * Enables this button
1376 this.disabled = false;
1377 this.el.removeClass('disabled');
1378 this.el.dom.removeAttribute("disabled");
1382 * Disable this button
1384 disable : function()
1386 this.disabled = true;
1387 this.el.addClass('disabled');
1388 this.el.attr("disabled", "disabled")
1391 * sets the active state on/off,
1392 * @param {Boolean} state (optional) Force a particular state
1394 setActive : function(v) {
1396 this.el[v ? 'addClass' : 'removeClass']('active');
1400 * toggles the current active state
1402 toggleActive : function(e)
1404 this.setActive(!this.pressed); // this modifies pressed...
1405 this.fireEvent('toggle', this, e, this.pressed);
1408 * get the current active state
1409 * @return {boolean} true if it's active
1411 isActive : function()
1413 return this.el.hasClass('active');
1416 * set the text of the first selected button
1418 setText : function(str)
1420 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1423 * get the text of the first selected button
1425 getText : function()
1427 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1430 setWeight : function(str)
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1433 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1435 var outline = this.outline ? 'outline-' : '';
1436 if (str == 'default') {
1437 this.el.addClass('btn-default btn-outline-secondary');
1440 this.el.addClass('btn-' + outline + str);
1445 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1447 Roo.bootstrap.Button.weights = [
1467 * @class Roo.bootstrap.Column
1468 * @extends Roo.bootstrap.Component
1469 * Bootstrap Column class
1470 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1471 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1472 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1473 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1474 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1475 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1476 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1477 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1480 * @cfg {Boolean} hidden (true|false) hide the element
1481 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1482 * @cfg {String} fa (ban|check|...) font awesome icon
1483 * @cfg {Number} fasize (1|2|....) font awsome size
1485 * @cfg {String} icon (info-sign|check|...) glyphicon name
1487 * @cfg {String} html content of column.
1490 * Create a new Column
1491 * @param {Object} config The config object
1494 Roo.bootstrap.Column = function(config){
1495 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1498 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1516 getAutoCreate : function(){
1517 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1525 var sizes = ['xs','sm','md','lg'];
1526 sizes.map(function(size ,ix){
1527 //Roo.log( size + ':' + settings[size]);
1529 if (settings[size+'off'] !== false) {
1530 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1533 if (settings[size] === false) {
1537 if (!settings[size]) { // 0 = hidden
1538 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1540 for (var i = ix; i > -1; i--) {
1541 cfg.cls += ' d-' + sizes[i] + '-none';
1547 cfg.cls += ' col-' + size + '-' + settings[size] + (
1548 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1554 cfg.cls += ' hidden';
1557 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1558 cfg.cls +=' alert alert-' + this.alert;
1562 if (this.html.length) {
1563 cfg.html = this.html;
1567 if (this.fasize > 1) {
1568 fasize = ' fa-' + this.fasize + 'x';
1570 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1575 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1594 * @class Roo.bootstrap.Container
1595 * @extends Roo.bootstrap.Component
1596 * Bootstrap Container class
1597 * @cfg {Boolean} jumbotron is it a jumbotron element
1598 * @cfg {String} html content of element
1599 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1600 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1601 * @cfg {String} header content of header (for panel)
1602 * @cfg {String} footer content of footer (for panel)
1603 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1604 * @cfg {String} tag (header|aside|section) type of HTML tag.
1605 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1606 * @cfg {String} fa font awesome icon
1607 * @cfg {String} icon (info-sign|check|...) glyphicon name
1608 * @cfg {Boolean} hidden (true|false) hide the element
1609 * @cfg {Boolean} expandable (true|false) default false
1610 * @cfg {Boolean} expanded (true|false) default true
1611 * @cfg {String} rheader contet on the right of header
1612 * @cfg {Boolean} clickable (true|false) default false
1616 * Create a new Container
1617 * @param {Object} config The config object
1620 Roo.bootstrap.Container = function(config){
1621 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1627 * After the panel has been expand
1629 * @param {Roo.bootstrap.Container} this
1634 * After the panel has been collapsed
1636 * @param {Roo.bootstrap.Container} this
1641 * When a element is chick
1642 * @param {Roo.bootstrap.Container} this
1643 * @param {Roo.EventObject} e
1649 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1667 getChildContainer : function() {
1673 if (this.panel.length) {
1674 return this.el.select('.panel-body',true).first();
1681 getAutoCreate : function(){
1684 tag : this.tag || 'div',
1688 if (this.jumbotron) {
1689 cfg.cls = 'jumbotron';
1694 // - this is applied by the parent..
1696 // cfg.cls = this.cls + '';
1699 if (this.sticky.length) {
1701 var bd = Roo.get(document.body);
1702 if (!bd.hasClass('bootstrap-sticky')) {
1703 bd.addClass('bootstrap-sticky');
1704 Roo.select('html',true).setStyle('height', '100%');
1707 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1711 if (this.well.length) {
1712 switch (this.well) {
1715 cfg.cls +=' well well-' +this.well;
1724 cfg.cls += ' hidden';
1728 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1729 cfg.cls +=' alert alert-' + this.alert;
1734 if (this.panel.length) {
1735 cfg.cls += ' panel panel-' + this.panel;
1737 if (this.header.length) {
1741 if(this.expandable){
1743 cfg.cls = cfg.cls + ' expandable';
1747 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1755 cls : 'panel-title',
1756 html : (this.expandable ? ' ' : '') + this.header
1760 cls: 'panel-header-right',
1766 cls : 'panel-heading',
1767 style : this.expandable ? 'cursor: pointer' : '',
1775 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1780 if (this.footer.length) {
1782 cls : 'panel-footer',
1791 body.html = this.html || cfg.html;
1792 // prefix with the icons..
1794 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1797 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1802 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1803 cfg.cls = 'container';
1809 initEvents: function()
1811 if(this.expandable){
1812 var headerEl = this.headerEl();
1815 headerEl.on('click', this.onToggleClick, this);
1820 this.el.on('click', this.onClick, this);
1825 onToggleClick : function()
1827 var headerEl = this.headerEl();
1843 if(this.fireEvent('expand', this)) {
1845 this.expanded = true;
1847 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1849 this.el.select('.panel-body',true).first().removeClass('hide');
1851 var toggleEl = this.toggleEl();
1857 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1862 collapse : function()
1864 if(this.fireEvent('collapse', this)) {
1866 this.expanded = false;
1868 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1869 this.el.select('.panel-body',true).first().addClass('hide');
1871 var toggleEl = this.toggleEl();
1877 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1881 toggleEl : function()
1883 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1887 return this.el.select('.panel-heading .fa',true).first();
1890 headerEl : function()
1892 if(!this.el || !this.panel.length || !this.header.length){
1896 return this.el.select('.panel-heading',true).first()
1901 if(!this.el || !this.panel.length){
1905 return this.el.select('.panel-body',true).first()
1908 titleEl : function()
1910 if(!this.el || !this.panel.length || !this.header.length){
1914 return this.el.select('.panel-title',true).first();
1917 setTitle : function(v)
1919 var titleEl = this.titleEl();
1925 titleEl.dom.innerHTML = v;
1928 getTitle : function()
1931 var titleEl = this.titleEl();
1937 return titleEl.dom.innerHTML;
1940 setRightTitle : function(v)
1942 var t = this.el.select('.panel-header-right',true).first();
1948 t.dom.innerHTML = v;
1951 onClick : function(e)
1955 this.fireEvent('click', this, e);
1962 * This is BS4's Card element.. - similar to our containers probably..
1966 * @class Roo.bootstrap.Card
1967 * @extends Roo.bootstrap.Component
1968 * Bootstrap Card class
1971 * possible... may not be implemented..
1972 * @cfg {String} header_image src url of image.
1973 * @cfg {String|Object} header
1974 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1975 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1977 * @cfg {String} title
1978 * @cfg {String} subtitle
1979 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1980 * @cfg {String} footer
1982 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1984 * @cfg {String} margin (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1990 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1992 * @cfg {String} padding (0|1|2|3|4|5)
1993 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1994 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1995 * @cfg {String} padding_left (0|1|2|3|4|5)
1996 * @cfg {String} padding_right (0|1|2|3|4|5)
1997 * @cfg {String} padding_x (0|1|2|3|4|5)
1998 * @cfg {String} padding_y (0|1|2|3|4|5)
2000 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2006 * @config {Boolean} dragable if this card can be dragged.
2007 * @config {String} drag_group group for drag
2008 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2009 * @config {String} drop_group group for drag
2011 * @config {Boolean} collapsable can the body be collapsed.
2012 * @config {Boolean} collapsed is the body collapsed when rendered...
2013 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2014 * @config {Boolean} rotated is the body rotated when rendered...
2017 * Create a new Container
2018 * @param {Object} config The config object
2021 Roo.bootstrap.Card = function(config){
2022 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2028 * When a element a card is dropped
2029 * @param {Roo.bootstrap.Card} this
2032 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2033 * @param {String} position 'above' or 'below'
2034 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2040 * When a element a card is rotate
2041 * @param {Roo.bootstrap.Card} this
2042 * @param {Roo.Element} n the node being dropped?
2043 * @param {Boolean} rotate status
2048 * When a card element is dragged over ready to drop (return false to block dropable)
2049 * @param {Roo.bootstrap.Card} this
2050 * @param {Object} data from dragdrop
2058 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2063 margin: '', /// may be better in component?
2093 collapsable : false,
2102 childContainer : false,
2103 dropEl : false, /// the dom placeholde element that indicates drop location.
2104 containerEl: false, // body container
2105 bodyEl: false, // card-body
2106 headerContainerEl : false, //
2108 header_imageEl : false,
2111 layoutCls : function()
2115 Roo.log(this.margin_bottom.length);
2116 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2117 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2119 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2120 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2122 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2123 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2127 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2128 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2129 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2133 // more generic support?
2141 // Roo.log("Call onRender: " + this.xtype);
2142 /* We are looking at something like this.
2144 <img src="..." class="card-img-top" alt="...">
2145 <div class="card-body">
2146 <h5 class="card-title">Card title</h5>
2147 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2149 >> this bit is really the body...
2150 <div> << we will ad dthis in hopefully it will not break shit.
2152 ** card text does not actually have any styling...
2154 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2157 <a href="#" class="card-link">Card link</a>
2160 <div class="card-footer">
2161 <small class="text-muted">Last updated 3 mins ago</small>
2165 getAutoCreate : function(){
2173 if (this.weight.length && this.weight != 'light') {
2174 cfg.cls += ' text-white';
2176 cfg.cls += ' text-dark'; // need as it's nested..
2178 if (this.weight.length) {
2179 cfg.cls += ' bg-' + this.weight;
2182 cfg.cls += ' ' + this.layoutCls();
2185 var hdr_ctr = false;
2186 if (this.header.length) {
2188 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2189 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2203 if (this.collapsable) {
2206 cls : 'd-block user-select-none',
2210 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2215 hdr.cn.push(hdr_ctr);
2220 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2225 if (this.header_image.length) {
2228 cls : 'card-img-top',
2229 src: this.header_image // escape?
2234 cls : 'card-img-top d-none'
2240 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2244 if (this.collapsable || this.rotateable) {
2247 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2254 if (this.title.length) {
2258 src: this.title // escape?
2262 if (this.subtitle.length) {
2266 src: this.subtitle // escape?
2272 cls : 'roo-card-body-ctr'
2275 if (this.html.length) {
2281 // fixme ? handle objects?
2283 if (this.footer.length) {
2286 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2291 cfg.cn.push({cls : 'card-footer d-none'});
2300 getCardHeader : function()
2302 var ret = this.el.select('.card-header',true).first();
2303 if (ret.hasClass('d-none')) {
2304 ret.removeClass('d-none');
2309 getCardFooter : function()
2311 var ret = this.el.select('.card-footer',true).first();
2312 if (ret.hasClass('d-none')) {
2313 ret.removeClass('d-none');
2318 getCardImageTop : function()
2320 var ret = this.header_imageEl;
2321 if (ret.hasClass('d-none')) {
2322 ret.removeClass('d-none');
2328 getChildContainer : function()
2334 return this.el.select('.roo-card-body-ctr',true).first();
2337 initEvents: function()
2339 this.bodyEl = this.el.select('.card-body',true).first();
2340 this.containerEl = this.getChildContainer();
2342 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2343 containerScroll: true,
2344 ddGroup: this.drag_group || 'default_card_drag_group'
2346 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2348 if (this.dropable) {
2349 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2350 containerScroll: true,
2351 ddGroup: this.drop_group || 'default_card_drag_group'
2353 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2354 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2355 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2356 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2357 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2360 if (this.collapsable) {
2361 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2363 if (this.rotateable) {
2364 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2366 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2368 this.footerEl = this.el.select('.card-footer',true).first();
2369 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2370 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2371 this.headerEl = this.el.select('.card-header',true).first();
2374 this.el.addClass('roo-card-rotated');
2375 this.fireEvent('rotate', this, true);
2377 this.header_imageEl = this.el.select('.card-img-top',true).first();
2378 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2381 getDragData : function(e)
2383 var target = this.getEl();
2385 //this.handleSelection(e);
2390 nodes: this.getEl(),
2395 dragData.ddel = target.dom ; // the div element
2396 Roo.log(target.getWidth( ));
2397 dragData.ddel.style.width = target.getWidth() + 'px';
2404 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2405 * whole Element becomes the target, and this causes the drop gesture to append.
2407 * Returns an object:
2410 position : 'below' or 'above'
2411 card : relateive to card OBJECT (or true for no cards listed)
2412 items_n : relative to nth item in list
2413 card_n : relative to nth card in list
2418 getTargetFromEvent : function(e, dragged_card_el)
2420 var target = e.getTarget();
2421 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2422 target = target.parentNode;
2433 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2434 // see if target is one of the 'cards'...
2437 //Roo.log(this.items.length);
2440 var last_card_n = 0;
2442 for (var i = 0;i< this.items.length;i++) {
2444 if (!this.items[i].el.hasClass('card')) {
2447 pos = this.getDropPoint(e, this.items[i].el.dom);
2449 cards_len = ret.cards.length;
2450 //Roo.log(this.items[i].el.dom.id);
2451 ret.cards.push(this.items[i]);
2453 if (ret.card_n < 0 && pos == 'above') {
2454 ret.position = cards_len > 0 ? 'below' : pos;
2455 ret.items_n = i > 0 ? i - 1 : 0;
2456 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2457 ret.card = ret.cards[ret.card_n];
2460 if (!ret.cards.length) {
2462 ret.position = 'below';
2466 // could not find a card.. stick it at the end..
2467 if (ret.card_n < 0) {
2468 ret.card_n = last_card_n;
2469 ret.card = ret.cards[last_card_n];
2470 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2471 ret.position = 'below';
2474 if (this.items[ret.items_n].el == dragged_card_el) {
2478 if (ret.position == 'below') {
2479 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2481 if (card_after && card_after.el == dragged_card_el) {
2488 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2490 if (card_before && card_before.el == dragged_card_el) {
2497 onNodeEnter : function(n, dd, e, data){
2500 onNodeOver : function(n, dd, e, data)
2503 var target_info = this.getTargetFromEvent(e,data.source.el);
2504 if (target_info === false) {
2505 this.dropPlaceHolder('hide');
2508 Roo.log(['getTargetFromEvent', target_info ]);
2511 if (this.fireEvent('cardover', this, [ data ]) === false) {
2515 this.dropPlaceHolder('show', target_info,data);
2519 onNodeOut : function(n, dd, e, data){
2520 this.dropPlaceHolder('hide');
2523 onNodeDrop : function(n, dd, e, data)
2526 // call drop - return false if
2528 // this could actually fail - if the Network drops..
2529 // we will ignore this at present..- client should probably reload
2530 // the whole set of cards if stuff like that fails.
2533 var info = this.getTargetFromEvent(e,data.source.el);
2534 if (info === false) {
2537 this.dropPlaceHolder('hide');
2541 this.acceptCard(data.source, info.position, info.card, info.items_n);
2545 firstChildCard : function()
2547 for (var i = 0;i< this.items.length;i++) {
2549 if (!this.items[i].el.hasClass('card')) {
2552 return this.items[i];
2554 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2559 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2561 acceptCard : function(move_card, position, next_to_card )
2563 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2567 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2569 move_card.parent().removeCard(move_card);
2572 var dom = move_card.el.dom;
2573 dom.style.width = ''; // clear with - which is set by drag.
2575 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2576 var cardel = next_to_card.el.dom;
2578 if (position == 'above' ) {
2579 cardel.parentNode.insertBefore(dom, cardel);
2580 } else if (cardel.nextSibling) {
2581 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2583 cardel.parentNode.append(dom);
2586 // card container???
2587 this.containerEl.dom.append(dom);
2590 //FIXME HANDLE card = true
2592 // add this to the correct place in items.
2594 // remove Card from items.
2597 if (this.items.length) {
2599 //Roo.log([info.items_n, info.position, this.items.length]);
2600 for (var i =0; i < this.items.length; i++) {
2601 if (i == to_items_n && position == 'above') {
2602 nitems.push(move_card);
2604 nitems.push(this.items[i]);
2605 if (i == to_items_n && position == 'below') {
2606 nitems.push(move_card);
2609 this.items = nitems;
2610 Roo.log(this.items);
2612 this.items.push(move_card);
2615 move_card.parentId = this.id;
2621 removeCard : function(c)
2623 this.items = this.items.filter(function(e) { return e != c });
2626 dom.parentNode.removeChild(dom);
2627 dom.style.width = ''; // clear with - which is set by drag.
2632 /** Decide whether to drop above or below a View node. */
2633 getDropPoint : function(e, n, dd)
2638 if (n == this.containerEl.dom) {
2641 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2642 var c = t + (b - t) / 2;
2643 var y = Roo.lib.Event.getPageY(e);
2650 onToggleCollapse : function(e)
2652 if (this.collapsed) {
2653 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2654 this.collapsableEl.addClass('show');
2655 this.collapsed = false;
2658 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2659 this.collapsableEl.removeClass('show');
2660 this.collapsed = true;
2665 onToggleRotate : function(e)
2667 this.collapsableEl.removeClass('show');
2668 this.footerEl.removeClass('d-none');
2669 this.el.removeClass('roo-card-rotated');
2670 this.el.removeClass('d-none');
2673 this.collapsableEl.addClass('show');
2674 this.rotated = false;
2675 this.fireEvent('rotate', this, this.rotated);
2678 this.el.addClass('roo-card-rotated');
2679 this.footerEl.addClass('d-none');
2680 this.el.select('.roo-collapsable').removeClass('show');
2682 this.rotated = true;
2683 this.fireEvent('rotate', this, this.rotated);
2687 dropPlaceHolder: function (action, info, data)
2689 if (this.dropEl === false) {
2690 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2694 this.dropEl.removeClass(['d-none', 'd-block']);
2695 if (action == 'hide') {
2697 this.dropEl.addClass('d-none');
2700 // FIXME - info.card == true!!!
2701 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2703 if (info.card !== true) {
2704 var cardel = info.card.el.dom;
2706 if (info.position == 'above') {
2707 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2708 } else if (cardel.nextSibling) {
2709 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2711 cardel.parentNode.append(this.dropEl.dom);
2714 // card container???
2715 this.containerEl.dom.append(this.dropEl.dom);
2718 this.dropEl.addClass('d-block roo-card-dropzone');
2720 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2727 setHeaderText: function(html)
2730 if (this.headerContainerEl) {
2731 this.headerContainerEl.dom.innerHTML = html;
2734 onHeaderImageLoad : function(ev, he)
2736 if (!this.header_image_fit_square) {
2740 var hw = he.naturalHeight / he.naturalWidth;
2743 //var w = he.dom.naturalWidth;
2746 he.style.position = 'relative';
2748 var nw = (ww * (1/hw));
2749 Roo.get(he).setSize( ww * (1/hw), ww);
2750 he.style.left = ((ww - nw)/ 2) + 'px';
2751 he.style.position = 'relative';
2762 * Card header - holder for the card header elements.
2767 * @class Roo.bootstrap.CardHeader
2768 * @extends Roo.bootstrap.Element
2769 * Bootstrap CardHeader class
2771 * Create a new Card Header - that you can embed children into
2772 * @param {Object} config The config object
2775 Roo.bootstrap.CardHeader = function(config){
2776 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2779 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2782 container_method : 'getCardHeader'
2795 * Card footer - holder for the card footer elements.
2800 * @class Roo.bootstrap.CardFooter
2801 * @extends Roo.bootstrap.Element
2802 * Bootstrap CardFooter class
2804 * Create a new Card Footer - that you can embed children into
2805 * @param {Object} config The config object
2808 Roo.bootstrap.CardFooter = function(config){
2809 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2812 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2815 container_method : 'getCardFooter'
2828 * Card header - holder for the card header elements.
2833 * @class Roo.bootstrap.CardImageTop
2834 * @extends Roo.bootstrap.Element
2835 * Bootstrap CardImageTop class
2837 * Create a new Card Image Top container
2838 * @param {Object} config The config object
2841 Roo.bootstrap.CardImageTop = function(config){
2842 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2845 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2848 container_method : 'getCardImageTop'
2863 * @class Roo.bootstrap.ButtonUploader
2864 * @extends Roo.bootstrap.Button
2865 * Bootstrap Button Uploader class - it's a button which when you add files to it
2868 * @cfg {Number} errorTimeout default 3000
2869 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2870 * @cfg {Array} html The button text.
2871 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2874 * Create a new CardUploader
2875 * @param {Object} config The config object
2878 Roo.bootstrap.ButtonUploader = function(config){
2882 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2888 * @event beforeselect
2889 * When button is pressed, before show upload files dialog is shown
2890 * @param {Roo.bootstrap.UploaderButton} this
2893 'beforeselect' : true,
2895 * @event fired when files have been selected,
2896 * When a the download link is clicked
2897 * @param {Roo.bootstrap.UploaderButton} this
2898 * @param {Array} Array of files that have been uploaded
2905 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2908 errorTimeout : 3000,
2912 fileCollection : false,
2917 getAutoCreate : function()
2922 cls : 'd-none roo-card-upload-selector'
2925 if (this.multiple) {
2926 im.multiple = 'multiple';
2932 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2942 initEvents : function()
2945 Roo.bootstrap.Button.prototype.initEvents.call(this);
2951 this.urlAPI = (window.createObjectURL && window) ||
2952 (window.URL && URL.revokeObjectURL && URL) ||
2953 (window.webkitURL && webkitURL);
2958 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2960 this.selectorEl.on('change', this.onFileSelected, this);
2967 onClick : function(e)
2971 if ( this.fireEvent('beforeselect', this) === false) {
2975 this.selectorEl.dom.click();
2979 onFileSelected : function(e)
2983 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2986 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2987 this.selectorEl.dom.value = '';// hopefully reset..
2989 this.fireEvent('uploaded', this, files );
2997 * addCard - add an Attachment to the uploader
2998 * @param data - the data about the image to upload
3002 title : "Title of file",
3003 is_uploaded : false,
3004 src : "http://.....",
3005 srcfile : { the File upload object },
3006 mimetype : file.type,
3009 .. any other data...
3034 * @class Roo.bootstrap.Img
3035 * @extends Roo.bootstrap.Component
3036 * Bootstrap Img class
3037 * @cfg {Boolean} imgResponsive false | true
3038 * @cfg {String} border rounded | circle | thumbnail
3039 * @cfg {String} src image source
3040 * @cfg {String} alt image alternative text
3041 * @cfg {String} href a tag href
3042 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3043 * @cfg {String} xsUrl xs image source
3044 * @cfg {String} smUrl sm image source
3045 * @cfg {String} mdUrl md image source
3046 * @cfg {String} lgUrl lg image source
3047 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3050 * Create a new Input
3051 * @param {Object} config The config object
3054 Roo.bootstrap.Img = function(config){
3055 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3061 * The img click event for the img.
3062 * @param {Roo.EventObject} e
3067 * The when any image loads
3068 * @param {Roo.EventObject} e
3074 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3076 imgResponsive: true,
3085 backgroundContain : false,
3087 getAutoCreate : function()
3089 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3090 return this.createSingleImg();
3095 cls: 'roo-image-responsive-group',
3100 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3102 if(!_this[size + 'Url']){
3108 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3109 html: _this.html || cfg.html,
3110 src: _this[size + 'Url']
3113 img.cls += ' roo-image-responsive-' + size;
3115 var s = ['xs', 'sm', 'md', 'lg'];
3117 s.splice(s.indexOf(size), 1);
3119 Roo.each(s, function(ss){
3120 img.cls += ' hidden-' + ss;
3123 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3124 cfg.cls += ' img-' + _this.border;
3128 cfg.alt = _this.alt;
3141 a.target = _this.target;
3145 cfg.cn.push((_this.href) ? a : img);
3152 createSingleImg : function()
3156 cls: (this.imgResponsive) ? 'img-responsive' : '',
3158 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3161 if (this.backgroundContain) {
3162 cfg.cls += ' background-contain';
3165 cfg.html = this.html || cfg.html;
3167 if (this.backgroundContain) {
3168 cfg.style="background-image: url(" + this.src + ')';
3170 cfg.src = this.src || cfg.src;
3173 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3174 cfg.cls += ' img-' + this.border;
3191 a.target = this.target;
3196 return (this.href) ? a : cfg;
3199 initEvents: function()
3202 this.el.on('click', this.onClick, this);
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.on('load', this.onImageLoad, this);
3207 // not sure if this works.. not tested
3208 this.el.select('img', true).on('load', this.onImageLoad, this);
3213 onClick : function(e)
3215 Roo.log('img onclick');
3216 this.fireEvent('click', this, e);
3218 onImageLoad: function(e)
3220 Roo.log('img load');
3221 this.fireEvent('load', this, e);
3225 * Sets the url of the image - used to update it
3226 * @param {String} url the url of the image
3229 setSrc : function(url)
3233 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3234 if (this.backgroundContain) {
3235 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3237 this.el.dom.src = url;
3242 this.el.select('img', true).first().dom.src = url;
3258 * @class Roo.bootstrap.Link
3259 * @extends Roo.bootstrap.Component
3260 * Bootstrap Link Class
3261 * @cfg {String} alt image alternative text
3262 * @cfg {String} href a tag href
3263 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3264 * @cfg {String} html the content of the link.
3265 * @cfg {String} anchor name for the anchor link
3266 * @cfg {String} fa - favicon
3268 * @cfg {Boolean} preventDefault (true | false) default false
3272 * Create a new Input
3273 * @param {Object} config The config object
3276 Roo.bootstrap.Link = function(config){
3277 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3283 * The img click event for the img.
3284 * @param {Roo.EventObject} e
3290 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3294 preventDefault: false,
3300 getAutoCreate : function()
3302 var html = this.html || '';
3304 if (this.fa !== false) {
3305 html = '<i class="fa fa-' + this.fa + '"></i>';
3310 // anchor's do not require html/href...
3311 if (this.anchor === false) {
3313 cfg.href = this.href || '#';
3315 cfg.name = this.anchor;
3316 if (this.html !== false || this.fa !== false) {
3319 if (this.href !== false) {
3320 cfg.href = this.href;
3324 if(this.alt !== false){
3329 if(this.target !== false) {
3330 cfg.target = this.target;
3336 initEvents: function() {
3338 if(!this.href || this.preventDefault){
3339 this.el.on('click', this.onClick, this);
3343 onClick : function(e)
3345 if(this.preventDefault){
3348 //Roo.log('img onclick');
3349 this.fireEvent('click', this, e);
3362 * @class Roo.bootstrap.Header
3363 * @extends Roo.bootstrap.Component
3364 * Bootstrap Header class
3365 * @cfg {String} html content of header
3366 * @cfg {Number} level (1|2|3|4|5|6) default 1
3369 * Create a new Header
3370 * @param {Object} config The config object
3374 Roo.bootstrap.Header = function(config){
3375 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3378 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3386 getAutoCreate : function(){
3391 tag: 'h' + (1 *this.level),
3392 html: this.html || ''
3404 * Ext JS Library 1.1.1
3405 * Copyright(c) 2006-2007, Ext JS, LLC.
3407 * Originally Released Under LGPL - original licence link has changed is not relivant.
3410 * <script type="text/javascript">
3414 * @class Roo.bootstrap.MenuMgr
3415 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3418 Roo.bootstrap.MenuMgr = function(){
3419 var menus, active, groups = {}, attached = false, lastShow = new Date();
3421 // private - called when first menu is created
3424 active = new Roo.util.MixedCollection();
3425 Roo.get(document).addKeyListener(27, function(){
3426 if(active.length > 0){
3434 if(active && active.length > 0){
3435 var c = active.clone();
3445 if(active.length < 1){
3446 Roo.get(document).un("mouseup", onMouseDown);
3454 var last = active.last();
3455 lastShow = new Date();
3458 Roo.get(document).on("mouseup", onMouseDown);
3463 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3464 m.parentMenu.activeChild = m;
3465 }else if(last && last.isVisible()){
3466 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3471 function onBeforeHide(m){
3473 m.activeChild.hide();
3475 if(m.autoHideTimer){
3476 clearTimeout(m.autoHideTimer);
3477 delete m.autoHideTimer;
3482 function onBeforeShow(m){
3483 var pm = m.parentMenu;
3484 if(!pm && !m.allowOtherMenus){
3486 }else if(pm && pm.activeChild && active != m){
3487 pm.activeChild.hide();
3491 // private this should really trigger on mouseup..
3492 function onMouseDown(e){
3493 Roo.log("on Mouse Up");
3495 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3496 Roo.log("MenuManager hideAll");
3505 function onBeforeCheck(mi, state){
3507 var g = groups[mi.group];
3508 for(var i = 0, l = g.length; i < l; i++){
3510 g[i].setChecked(false);
3519 * Hides all menus that are currently visible
3521 hideAll : function(){
3526 register : function(menu){
3530 menus[menu.id] = menu;
3531 menu.on("beforehide", onBeforeHide);
3532 menu.on("hide", onHide);
3533 menu.on("beforeshow", onBeforeShow);
3534 menu.on("show", onShow);
3536 if(g && menu.events["checkchange"]){
3540 groups[g].push(menu);
3541 menu.on("checkchange", onCheck);
3546 * Returns a {@link Roo.menu.Menu} object
3547 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3548 * be used to generate and return a new Menu instance.
3550 get : function(menu){
3551 if(typeof menu == "string"){ // menu id
3553 }else if(menu.events){ // menu instance
3556 /*else if(typeof menu.length == 'number'){ // array of menu items?
3557 return new Roo.bootstrap.Menu({items:menu});
3558 }else{ // otherwise, must be a config
3559 return new Roo.bootstrap.Menu(menu);
3566 unregister : function(menu){
3567 delete menus[menu.id];
3568 menu.un("beforehide", onBeforeHide);
3569 menu.un("hide", onHide);
3570 menu.un("beforeshow", onBeforeShow);
3571 menu.un("show", onShow);
3573 if(g && menu.events["checkchange"]){
3574 groups[g].remove(menu);
3575 menu.un("checkchange", onCheck);
3580 registerCheckable : function(menuItem){
3581 var g = menuItem.group;
3586 groups[g].push(menuItem);
3587 menuItem.on("beforecheckchange", onBeforeCheck);
3592 unregisterCheckable : function(menuItem){
3593 var g = menuItem.group;
3595 groups[g].remove(menuItem);
3596 menuItem.un("beforecheckchange", onBeforeCheck);
3608 * @class Roo.bootstrap.Menu
3609 * @extends Roo.bootstrap.Component
3610 * Bootstrap Menu class - container for MenuItems
3611 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3612 * @cfg {bool} hidden if the menu should be hidden when rendered.
3613 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3614 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3615 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3616 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3620 * @param {Object} config The config object
3624 Roo.bootstrap.Menu = function(config){
3626 if (config.type == 'treeview') {
3627 // normally menu's are drawn attached to the document to handle layering etc..
3628 // however treeview (used by the docs menu is drawn into the parent element)
3629 this.container_method = 'getChildContainer';
3632 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3633 if (this.registerMenu && this.type != 'treeview') {
3634 Roo.bootstrap.MenuMgr.register(this);
3641 * Fires before this menu is displayed (return false to block)
3642 * @param {Roo.menu.Menu} this
3647 * Fires before this menu is hidden (return false to block)
3648 * @param {Roo.menu.Menu} this
3653 * Fires after this menu is displayed
3654 * @param {Roo.menu.Menu} this
3659 * Fires after this menu is hidden
3660 * @param {Roo.menu.Menu} this
3665 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3666 * @param {Roo.menu.Menu} this
3667 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3668 * @param {Roo.EventObject} e
3673 * Fires when the mouse is hovering over this menu
3674 * @param {Roo.menu.Menu} this
3675 * @param {Roo.EventObject} e
3676 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681 * Fires when the mouse exits this menu
3682 * @param {Roo.menu.Menu} this
3683 * @param {Roo.EventObject} e
3684 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689 * Fires when a menu item contained in this menu is clicked
3690 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3691 * @param {Roo.EventObject} e
3695 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3698 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3702 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3705 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3707 registerMenu : true,
3709 menuItems :false, // stores the menu items..
3719 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3721 hideTrigger : false,
3726 getChildContainer : function() {
3730 getAutoCreate : function(){
3732 //if (['right'].indexOf(this.align)!==-1) {
3733 // cfg.cn[1].cls += ' pull-right'
3738 cls : 'dropdown-menu shadow' ,
3739 style : 'z-index:1000'
3743 if (this.type === 'submenu') {
3744 cfg.cls = 'submenu active';
3746 if (this.type === 'treeview') {
3747 cfg.cls = 'treeview-menu';
3752 initEvents : function() {
3754 // Roo.log("ADD event");
3755 // Roo.log(this.triggerEl.dom);
3756 if (this.triggerEl) {
3758 this.triggerEl.on('click', this.onTriggerClick, this);
3760 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3762 if (!this.hideTrigger) {
3763 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3764 // dropdown toggle on the 'a' in BS4?
3765 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3767 this.triggerEl.addClass('dropdown-toggle');
3773 this.el.on('touchstart' , this.onTouch, this);
3775 this.el.on('click' , this.onClick, this);
3777 this.el.on("mouseover", this.onMouseOver, this);
3778 this.el.on("mouseout", this.onMouseOut, this);
3782 findTargetItem : function(e)
3784 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3788 //Roo.log(t); Roo.log(t.id);
3790 //Roo.log(this.menuitems);
3791 return this.menuitems.get(t.id);
3793 //return this.items.get(t.menuItemId);
3799 onTouch : function(e)
3801 Roo.log("menu.onTouch");
3802 //e.stopEvent(); this make the user popdown broken
3806 onClick : function(e)
3808 Roo.log("menu.onClick");
3810 var t = this.findTargetItem(e);
3811 if(!t || t.isContainer){
3816 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3817 if(t == this.activeItem && t.shouldDeactivate(e)){
3818 this.activeItem.deactivate();
3819 delete this.activeItem;
3823 this.setActiveItem(t, true);
3831 Roo.log('pass click event');
3835 this.fireEvent("click", this, t, e);
3839 if(!t.href.length || t.href == '#'){
3840 (function() { _this.hide(); }).defer(100);
3845 onMouseOver : function(e){
3846 var t = this.findTargetItem(e);
3849 // if(t.canActivate && !t.disabled){
3850 // this.setActiveItem(t, true);
3854 this.fireEvent("mouseover", this, e, t);
3856 isVisible : function(){
3857 return !this.hidden;
3859 onMouseOut : function(e){
3860 var t = this.findTargetItem(e);
3863 // if(t == this.activeItem && t.shouldDeactivate(e)){
3864 // this.activeItem.deactivate();
3865 // delete this.activeItem;
3868 this.fireEvent("mouseout", this, e, t);
3873 * Displays this menu relative to another element
3874 * @param {String/HTMLElement/Roo.Element} element The element to align to
3875 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3876 * the element (defaults to this.defaultAlign)
3877 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3879 show : function(el, pos, parentMenu)
3881 if (false === this.fireEvent("beforeshow", this)) {
3882 Roo.log("show canceled");
3885 this.parentMenu = parentMenu;
3889 this.el.addClass('show'); // show otherwise we do not know how big we are..
3891 var xy = this.el.getAlignToXY(el, pos);
3893 // bl-tl << left align below
3894 // tl-bl << left align
3896 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3897 // if it goes to far to the right.. -> align left.
3898 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3901 // was left align - go right?
3902 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3905 // goes down the bottom
3906 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3908 var a = this.align.replace('?', '').split('-');
3909 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3913 this.showAt( xy , parentMenu, false);
3916 * Displays this menu at a specific xy position
3917 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3918 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3920 showAt : function(xy, parentMenu, /* private: */_e){
3921 this.parentMenu = parentMenu;
3926 this.fireEvent("beforeshow", this);
3927 //xy = this.el.adjustForConstraints(xy);
3931 this.hideMenuItems();
3932 this.hidden = false;
3933 if (this.triggerEl) {
3934 this.triggerEl.addClass('open');
3937 this.el.addClass('show');
3941 // reassign x when hitting right
3943 // reassign y when hitting bottom
3945 // but the list may align on trigger left or trigger top... should it be a properity?
3947 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3952 this.fireEvent("show", this);
3958 this.doFocus.defer(50, this);
3962 doFocus : function(){
3964 this.focusEl.focus();
3969 * Hides this menu and optionally all parent menus
3970 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3972 hide : function(deep)
3974 if (false === this.fireEvent("beforehide", this)) {
3975 Roo.log("hide canceled");
3978 this.hideMenuItems();
3979 if(this.el && this.isVisible()){
3981 if(this.activeItem){
3982 this.activeItem.deactivate();
3983 this.activeItem = null;
3985 if (this.triggerEl) {
3986 this.triggerEl.removeClass('open');
3989 this.el.removeClass('show');
3991 this.fireEvent("hide", this);
3993 if(deep === true && this.parentMenu){
3994 this.parentMenu.hide(true);
3998 onTriggerClick : function(e)
4000 Roo.log('trigger click');
4002 var target = e.getTarget();
4004 Roo.log(target.nodeName.toLowerCase());
4006 if(target.nodeName.toLowerCase() === 'i'){
4012 onTriggerPress : function(e)
4014 Roo.log('trigger press');
4015 //Roo.log(e.getTarget());
4016 // Roo.log(this.triggerEl.dom);
4018 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4019 var pel = Roo.get(e.getTarget());
4020 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4021 Roo.log('is treeview or dropdown?');
4025 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4029 if (this.isVisible()) {
4035 this.show(this.triggerEl, this.align, false);
4038 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4045 hideMenuItems : function()
4047 Roo.log("hide Menu Items");
4052 this.el.select('.open',true).each(function(aa) {
4054 aa.removeClass('open');
4058 addxtypeChild : function (tree, cntr) {
4059 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4061 this.menuitems.add(comp);
4073 this.getEl().dom.innerHTML = '';
4074 this.menuitems.clear();
4088 * @class Roo.bootstrap.MenuItem
4089 * @extends Roo.bootstrap.Component
4090 * Bootstrap MenuItem class
4091 * @cfg {String} html the menu label
4092 * @cfg {String} href the link
4093 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4094 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4095 * @cfg {Boolean} active used on sidebars to highlight active itesm
4096 * @cfg {String} fa favicon to show on left of menu item.
4097 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4101 * Create a new MenuItem
4102 * @param {Object} config The config object
4106 Roo.bootstrap.MenuItem = function(config){
4107 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4112 * The raw click event for the entire grid.
4113 * @param {Roo.bootstrap.MenuItem} this
4114 * @param {Roo.EventObject} e
4120 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4124 preventDefault: false,
4125 isContainer : false,
4129 getAutoCreate : function(){
4131 if(this.isContainer){
4134 cls: 'dropdown-menu-item '
4144 cls : 'dropdown-item',
4149 if (this.fa !== false) {
4152 cls : 'fa fa-' + this.fa
4161 cls: 'dropdown-menu-item',
4164 if (this.parent().type == 'treeview') {
4165 cfg.cls = 'treeview-menu';
4168 cfg.cls += ' active';
4173 anc.href = this.href || cfg.cn[0].href ;
4174 ctag.html = this.html || cfg.cn[0].html ;
4178 initEvents: function()
4180 if (this.parent().type == 'treeview') {
4181 this.el.select('a').on('click', this.onClick, this);
4185 this.menu.parentType = this.xtype;
4186 this.menu.triggerEl = this.el;
4187 this.menu = this.addxtype(Roo.apply({}, this.menu));
4191 onClick : function(e)
4193 Roo.log('item on click ');
4195 if(this.preventDefault){
4198 //this.parent().hideMenuItems();
4200 this.fireEvent('click', this, e);
4219 * @class Roo.bootstrap.MenuSeparator
4220 * @extends Roo.bootstrap.Component
4221 * Bootstrap MenuSeparator class
4224 * Create a new MenuItem
4225 * @param {Object} config The config object
4229 Roo.bootstrap.MenuSeparator = function(config){
4230 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4233 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4235 getAutoCreate : function(){
4254 * @class Roo.bootstrap.Modal
4255 * @extends Roo.bootstrap.Component
4256 * Bootstrap Modal class
4257 * @cfg {String} title Title of dialog
4258 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4259 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4260 * @cfg {Boolean} specificTitle default false
4261 * @cfg {Array} buttons Array of buttons or standard button set..
4262 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4263 * @cfg {Boolean} animate default true
4264 * @cfg {Boolean} allow_close default true
4265 * @cfg {Boolean} fitwindow default false
4266 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4267 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4268 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4269 * @cfg {String} size (sm|lg|xl) default empty
4270 * @cfg {Number} max_width set the max width of modal
4271 * @cfg {Boolean} editableTitle can the title be edited
4276 * Create a new Modal Dialog
4277 * @param {Object} config The config object
4280 Roo.bootstrap.Modal = function(config){
4281 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4286 * The raw btnclick event for the button
4287 * @param {Roo.EventObject} e
4292 * Fire when dialog resize
4293 * @param {Roo.bootstrap.Modal} this
4294 * @param {Roo.EventObject} e
4298 * @event titlechanged
4299 * Fire when the editable title has been changed
4300 * @param {Roo.bootstrap.Modal} this
4301 * @param {Roo.EventObject} value
4303 "titlechanged" : true
4306 this.buttons = this.buttons || [];
4309 this.tmpl = Roo.factory(this.tmpl);
4314 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4316 title : 'test dialog',
4326 specificTitle: false,
4328 buttonPosition: 'right',
4350 editableTitle : false,
4352 onRender : function(ct, position)
4354 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4357 var cfg = Roo.apply({}, this.getAutoCreate());
4360 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4362 //if (!cfg.name.length) {
4366 cfg.cls += ' ' + this.cls;
4369 cfg.style = this.style;
4371 this.el = Roo.get(document.body).createChild(cfg, position);
4373 //var type = this.el.dom.type;
4376 if(this.tabIndex !== undefined){
4377 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4380 this.dialogEl = this.el.select('.modal-dialog',true).first();
4381 this.bodyEl = this.el.select('.modal-body',true).first();
4382 this.closeEl = this.el.select('.modal-header .close', true).first();
4383 this.headerEl = this.el.select('.modal-header',true).first();
4384 this.titleEl = this.el.select('.modal-title',true).first();
4385 this.footerEl = this.el.select('.modal-footer',true).first();
4387 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4389 //this.el.addClass("x-dlg-modal");
4391 if (this.buttons.length) {
4392 Roo.each(this.buttons, function(bb) {
4393 var b = Roo.apply({}, bb);
4394 b.xns = b.xns || Roo.bootstrap;
4395 b.xtype = b.xtype || 'Button';
4396 if (typeof(b.listeners) == 'undefined') {
4397 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4400 var btn = Roo.factory(b);
4402 btn.render(this.getButtonContainer());
4406 // render the children.
4409 if(typeof(this.items) != 'undefined'){
4410 var items = this.items;
4413 for(var i =0;i < items.length;i++) {
4414 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4418 this.items = nitems;
4420 // where are these used - they used to be body/close/footer
4424 //this.el.addClass([this.fieldClass, this.cls]);
4428 getAutoCreate : function()
4430 // we will default to modal-body-overflow - might need to remove or make optional later.
4432 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4433 html : this.html || ''
4438 cls : 'modal-title',
4442 if(this.specificTitle){ // WTF is this?
4447 if (this.allow_close && Roo.bootstrap.version == 3) {
4457 if (this.editableTitle) {
4459 cls: 'form-control roo-editable-title d-none',
4465 if (this.allow_close && Roo.bootstrap.version == 4) {
4475 if(this.size.length){
4476 size = 'modal-' + this.size;
4479 var footer = Roo.bootstrap.version == 3 ?
4481 cls : 'modal-footer',
4485 cls: 'btn-' + this.buttonPosition
4490 { // BS4 uses mr-auto on left buttons....
4491 cls : 'modal-footer'
4502 cls: "modal-dialog " + size,
4505 cls : "modal-content",
4508 cls : 'modal-header',
4523 modal.cls += ' fade';
4529 getChildContainer : function() {
4534 getButtonContainer : function() {
4536 return Roo.bootstrap.version == 4 ?
4537 this.el.select('.modal-footer',true).first()
4538 : this.el.select('.modal-footer div',true).first();
4541 initEvents : function()
4543 if (this.allow_close) {
4544 this.closeEl.on('click', this.hide, this);
4546 Roo.EventManager.onWindowResize(this.resize, this, true);
4547 if (this.editableTitle) {
4548 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4549 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4550 this.headerEditEl.on('keyup', function(e) {
4551 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4552 this.toggleHeaderInput(false)
4555 this.headerEditEl.on('blur', function(e) {
4556 this.toggleHeaderInput(false)
4565 this.maskEl.setSize(
4566 Roo.lib.Dom.getViewWidth(true),
4567 Roo.lib.Dom.getViewHeight(true)
4570 if (this.fitwindow) {
4572 this.dialogEl.setStyle( { 'max-width' : '100%' });
4574 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4575 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4580 if(this.max_width !== 0) {
4582 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4585 this.setSize(w, this.height);
4589 if(this.max_height) {
4590 this.setSize(w,Math.min(
4592 Roo.lib.Dom.getViewportHeight(true) - 60
4598 if(!this.fit_content) {
4599 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4603 this.setSize(w, Math.min(
4605 this.headerEl.getHeight() +
4606 this.footerEl.getHeight() +
4607 this.getChildHeight(this.bodyEl.dom.childNodes),
4608 Roo.lib.Dom.getViewportHeight(true) - 60)
4614 setSize : function(w,h)
4625 if (!this.rendered) {
4628 this.toggleHeaderInput(false);
4629 //this.el.setStyle('display', 'block');
4630 this.el.removeClass('hideing');
4631 this.el.dom.style.display='block';
4633 Roo.get(document.body).addClass('modal-open');
4635 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4638 this.el.addClass('show');
4639 this.el.addClass('in');
4642 this.el.addClass('show');
4643 this.el.addClass('in');
4646 // not sure how we can show data in here..
4648 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4651 Roo.get(document.body).addClass("x-body-masked");
4653 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4654 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4655 this.maskEl.dom.style.display = 'block';
4656 this.maskEl.addClass('show');
4661 this.fireEvent('show', this);
4663 // set zindex here - otherwise it appears to be ignored...
4664 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4667 this.items.forEach( function(e) {
4668 e.layout ? e.layout() : false;
4676 if(this.fireEvent("beforehide", this) !== false){
4678 this.maskEl.removeClass('show');
4680 this.maskEl.dom.style.display = '';
4681 Roo.get(document.body).removeClass("x-body-masked");
4682 this.el.removeClass('in');
4683 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4685 if(this.animate){ // why
4686 this.el.addClass('hideing');
4687 this.el.removeClass('show');
4689 if (!this.el.hasClass('hideing')) {
4690 return; // it's been shown again...
4693 this.el.dom.style.display='';
4695 Roo.get(document.body).removeClass('modal-open');
4696 this.el.removeClass('hideing');
4700 this.el.removeClass('show');
4701 this.el.dom.style.display='';
4702 Roo.get(document.body).removeClass('modal-open');
4705 this.fireEvent('hide', this);
4708 isVisible : function()
4711 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4715 addButton : function(str, cb)
4719 var b = Roo.apply({}, { html : str } );
4720 b.xns = b.xns || Roo.bootstrap;
4721 b.xtype = b.xtype || 'Button';
4722 if (typeof(b.listeners) == 'undefined') {
4723 b.listeners = { click : cb.createDelegate(this) };
4726 var btn = Roo.factory(b);
4728 btn.render(this.getButtonContainer());
4734 setDefaultButton : function(btn)
4736 //this.el.select('.modal-footer').()
4739 resizeTo: function(w,h)
4741 this.dialogEl.setWidth(w);
4743 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4745 this.bodyEl.setHeight(h - diff);
4747 this.fireEvent('resize', this);
4750 setContentSize : function(w, h)
4754 onButtonClick: function(btn,e)
4757 this.fireEvent('btnclick', btn.name, e);
4760 * Set the title of the Dialog
4761 * @param {String} str new Title
4763 setTitle: function(str) {
4764 this.titleEl.dom.innerHTML = str;
4768 * Set the body of the Dialog
4769 * @param {String} str new Title
4771 setBody: function(str) {
4772 this.bodyEl.dom.innerHTML = str;
4775 * Set the body of the Dialog using the template
4776 * @param {Obj} data - apply this data to the template and replace the body contents.
4778 applyBody: function(obj)
4781 Roo.log("Error - using apply Body without a template");
4784 this.tmpl.overwrite(this.bodyEl, obj);
4787 getChildHeight : function(child_nodes)
4791 child_nodes.length == 0
4796 var child_height = 0;
4798 for(var i = 0; i < child_nodes.length; i++) {
4801 * for modal with tabs...
4802 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4804 var layout_childs = child_nodes[i].childNodes;
4806 for(var j = 0; j < layout_childs.length; j++) {
4808 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4810 var layout_body_childs = layout_childs[j].childNodes;
4812 for(var k = 0; k < layout_body_childs.length; k++) {
4814 if(layout_body_childs[k].classList.contains('navbar')) {
4815 child_height += layout_body_childs[k].offsetHeight;
4819 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4821 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4823 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4825 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4826 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4841 child_height += child_nodes[i].offsetHeight;
4842 // Roo.log(child_nodes[i].offsetHeight);
4845 return child_height;
4847 toggleHeaderInput : function(is_edit)
4849 if (!this.editableTitle) {
4850 return; // not editable.
4852 if (is_edit && this.is_header_editing) {
4853 return; // already editing..
4857 this.headerEditEl.dom.value = this.title;
4858 this.headerEditEl.removeClass('d-none');
4859 this.headerEditEl.dom.focus();
4860 this.titleEl.addClass('d-none');
4862 this.is_header_editing = true;
4865 // flip back to not editing.
4866 this.title = this.headerEditEl.dom.value;
4867 this.headerEditEl.addClass('d-none');
4868 this.titleEl.removeClass('d-none');
4869 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4870 this.is_header_editing = false;
4871 this.fireEvent('titlechanged', this, this.title);
4880 Roo.apply(Roo.bootstrap.Modal, {
4882 * Button config that displays a single OK button
4891 * Button config that displays Yes and No buttons
4907 * Button config that displays OK and Cancel buttons
4922 * Button config that displays Yes, No and Cancel buttons
4947 * messagebox - can be used as a replace
4951 * @class Roo.MessageBox
4952 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4956 Roo.Msg.alert('Status', 'Changes saved successfully.');
4958 // Prompt for user data:
4959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4961 // process text value...
4965 // Show a dialog using config options:
4967 title:'Save Changes?',
4968 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4969 buttons: Roo.Msg.YESNOCANCEL,
4976 Roo.bootstrap.MessageBox = function(){
4977 var dlg, opt, mask, waitTimer;
4978 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4979 var buttons, activeTextEl, bwidth;
4983 var handleButton = function(button){
4985 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4989 var handleHide = function(){
4991 dlg.el.removeClass(opt.cls);
4994 // Roo.TaskMgr.stop(waitTimer);
4995 // waitTimer = null;
5000 var updateButtons = function(b){
5003 buttons["ok"].hide();
5004 buttons["cancel"].hide();
5005 buttons["yes"].hide();
5006 buttons["no"].hide();
5007 dlg.footerEl.hide();
5011 dlg.footerEl.show();
5012 for(var k in buttons){
5013 if(typeof buttons[k] != "function"){
5016 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5017 width += buttons[k].el.getWidth()+15;
5027 var handleEsc = function(d, k, e){
5028 if(opt && opt.closable !== false){
5038 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5039 * @return {Roo.BasicDialog} The BasicDialog element
5041 getDialog : function(){
5043 dlg = new Roo.bootstrap.Modal( {
5046 //constraintoviewport:false,
5048 //collapsible : false,
5053 //buttonAlign:"center",
5054 closeClick : function(){
5055 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5058 handleButton("cancel");
5063 dlg.on("hide", handleHide);
5065 //dlg.addKeyListener(27, handleEsc);
5067 this.buttons = buttons;
5068 var bt = this.buttonText;
5069 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5070 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5071 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5072 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5074 bodyEl = dlg.bodyEl.createChild({
5076 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5077 '<textarea class="roo-mb-textarea"></textarea>' +
5078 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5080 msgEl = bodyEl.dom.firstChild;
5081 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5082 textboxEl.enableDisplayMode();
5083 textboxEl.addKeyListener([10,13], function(){
5084 if(dlg.isVisible() && opt && opt.buttons){
5087 }else if(opt.buttons.yes){
5088 handleButton("yes");
5092 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5093 textareaEl.enableDisplayMode();
5094 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5095 progressEl.enableDisplayMode();
5097 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5098 var pf = progressEl.dom.firstChild;
5100 pp = Roo.get(pf.firstChild);
5101 pp.setHeight(pf.offsetHeight);
5109 * Updates the message box body text
5110 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5111 * the XHTML-compliant non-breaking space character '&#160;')
5112 * @return {Roo.MessageBox} This message box
5114 updateText : function(text)
5116 if(!dlg.isVisible() && !opt.width){
5117 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5118 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5120 msgEl.innerHTML = text || ' ';
5122 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5123 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5125 Math.min(opt.width || cw , this.maxWidth),
5126 Math.max(opt.minWidth || this.minWidth, bwidth)
5129 activeTextEl.setWidth(w);
5131 if(dlg.isVisible()){
5132 dlg.fixedcenter = false;
5134 // to big, make it scroll. = But as usual stupid IE does not support
5137 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5138 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5139 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5141 bodyEl.dom.style.height = '';
5142 bodyEl.dom.style.overflowY = '';
5145 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5147 bodyEl.dom.style.overflowX = '';
5150 dlg.setContentSize(w, bodyEl.getHeight());
5151 if(dlg.isVisible()){
5152 dlg.fixedcenter = true;
5158 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5159 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5160 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5161 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5162 * @return {Roo.MessageBox} This message box
5164 updateProgress : function(value, text){
5166 this.updateText(text);
5169 if (pp) { // weird bug on my firefox - for some reason this is not defined
5170 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5171 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5177 * Returns true if the message box is currently displayed
5178 * @return {Boolean} True if the message box is visible, else false
5180 isVisible : function(){
5181 return dlg && dlg.isVisible();
5185 * Hides the message box if it is displayed
5188 if(this.isVisible()){
5194 * Displays a new message box, or reinitializes an existing message box, based on the config options
5195 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5196 * The following config object properties are supported:
5198 Property Type Description
5199 ---------- --------------- ------------------------------------------------------------------------------------
5200 animEl String/Element An id or Element from which the message box should animate as it opens and
5201 closes (defaults to undefined)
5202 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5203 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5204 closable Boolean False to hide the top-right close button (defaults to true). Note that
5205 progress and wait dialogs will ignore this property and always hide the
5206 close button as they can only be closed programmatically.
5207 cls String A custom CSS class to apply to the message box element
5208 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5209 displayed (defaults to 75)
5210 fn Function A callback function to execute after closing the dialog. The arguments to the
5211 function will be btn (the name of the button that was clicked, if applicable,
5212 e.g. "ok"), and text (the value of the active text field, if applicable).
5213 Progress and wait dialogs will ignore this option since they do not respond to
5214 user actions and can only be closed programmatically, so any required function
5215 should be called by the same code after it closes the dialog.
5216 icon String A CSS class that provides a background image to be used as an icon for
5217 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5218 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5219 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5220 modal Boolean False to allow user interaction with the page while the message box is
5221 displayed (defaults to true)
5222 msg String A string that will replace the existing message box body text (defaults
5223 to the XHTML-compliant non-breaking space character ' ')
5224 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5225 progress Boolean True to display a progress bar (defaults to false)
5226 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5227 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5228 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5229 title String The title text
5230 value String The string value to set into the active textbox element if displayed
5231 wait Boolean True to display a progress bar (defaults to false)
5232 width Number The width of the dialog in pixels
5239 msg: 'Please enter your address:',
5241 buttons: Roo.MessageBox.OKCANCEL,
5244 animEl: 'addAddressBtn'
5247 * @param {Object} config Configuration options
5248 * @return {Roo.MessageBox} This message box
5250 show : function(options)
5253 // this causes nightmares if you show one dialog after another
5254 // especially on callbacks..
5256 if(this.isVisible()){
5259 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5260 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5261 Roo.log("New Dialog Message:" + options.msg )
5262 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5263 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5266 var d = this.getDialog();
5268 d.setTitle(opt.title || " ");
5269 d.closeEl.setDisplayed(opt.closable !== false);
5270 activeTextEl = textboxEl;
5271 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5276 textareaEl.setHeight(typeof opt.multiline == "number" ?
5277 opt.multiline : this.defaultTextHeight);
5278 activeTextEl = textareaEl;
5287 progressEl.setDisplayed(opt.progress === true);
5289 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5291 this.updateProgress(0);
5292 activeTextEl.dom.value = opt.value || "";
5294 dlg.setDefaultButton(activeTextEl);
5296 var bs = opt.buttons;
5300 }else if(bs && bs.yes){
5301 db = buttons["yes"];
5303 dlg.setDefaultButton(db);
5305 bwidth = updateButtons(opt.buttons);
5306 this.updateText(opt.msg);
5308 d.el.addClass(opt.cls);
5310 d.proxyDrag = opt.proxyDrag === true;
5311 d.modal = opt.modal !== false;
5312 d.mask = opt.modal !== false ? mask : false;
5314 // force it to the end of the z-index stack so it gets a cursor in FF
5315 document.body.appendChild(dlg.el.dom);
5316 d.animateTarget = null;
5317 d.show(options.animEl);
5323 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5324 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5325 * and closing the message box when the process is complete.
5326 * @param {String} title The title bar text
5327 * @param {String} msg The message box body text
5328 * @return {Roo.MessageBox} This message box
5330 progress : function(title, msg){
5337 minWidth: this.minProgressWidth,
5344 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5345 * If a callback function is passed it will be called after the user clicks the button, and the
5346 * id of the button that was clicked will be passed as the only parameter to the callback
5347 * (could also be the top-right close button).
5348 * @param {String} title The title bar text
5349 * @param {String} msg The message box body text
5350 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5351 * @param {Object} scope (optional) The scope of the callback function
5352 * @return {Roo.MessageBox} This message box
5354 alert : function(title, msg, fn, scope)
5369 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5370 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5371 * You are responsible for closing the message box when the process is complete.
5372 * @param {String} msg The message box body text
5373 * @param {String} title (optional) The title bar text
5374 * @return {Roo.MessageBox} This message box
5376 wait : function(msg, title){
5387 waitTimer = Roo.TaskMgr.start({
5389 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5397 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5398 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5399 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5400 * @param {String} title The title bar text
5401 * @param {String} msg The message box body text
5402 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5403 * @param {Object} scope (optional) The scope of the callback function
5404 * @return {Roo.MessageBox} This message box
5406 confirm : function(title, msg, fn, scope){
5410 buttons: this.YESNO,
5419 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5420 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5421 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5422 * (could also be the top-right close button) and the text that was entered will be passed as the two
5423 * parameters to the callback.
5424 * @param {String} title The title bar text
5425 * @param {String} msg The message box body text
5426 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427 * @param {Object} scope (optional) The scope of the callback function
5428 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5429 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5430 * @return {Roo.MessageBox} This message box
5432 prompt : function(title, msg, fn, scope, multiline){
5436 buttons: this.OKCANCEL,
5441 multiline: multiline,
5448 * Button config that displays a single OK button
5453 * Button config that displays Yes and No buttons
5456 YESNO : {yes:true, no:true},
5458 * Button config that displays OK and Cancel buttons
5461 OKCANCEL : {ok:true, cancel:true},
5463 * Button config that displays Yes, No and Cancel buttons
5466 YESNOCANCEL : {yes:true, no:true, cancel:true},
5469 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5472 defaultTextHeight : 75,
5474 * The maximum width in pixels of the message box (defaults to 600)
5479 * The minimum width in pixels of the message box (defaults to 100)
5484 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5485 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5488 minProgressWidth : 250,
5490 * An object containing the default button text strings that can be overriden for localized language support.
5491 * Supported properties are: ok, cancel, yes and no.
5492 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5505 * Shorthand for {@link Roo.MessageBox}
5507 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5508 Roo.Msg = Roo.Msg || Roo.MessageBox;
5517 * @class Roo.bootstrap.Navbar
5518 * @extends Roo.bootstrap.Component
5519 * Bootstrap Navbar class
5522 * Create a new Navbar
5523 * @param {Object} config The config object
5527 Roo.bootstrap.Navbar = function(config){
5528 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5532 * @event beforetoggle
5533 * Fire before toggle the menu
5534 * @param {Roo.EventObject} e
5536 "beforetoggle" : true
5540 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5549 getAutoCreate : function(){
5552 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5556 initEvents :function ()
5558 //Roo.log(this.el.select('.navbar-toggle',true));
5559 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5566 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5568 var size = this.el.getSize();
5569 this.maskEl.setSize(size.width, size.height);
5570 this.maskEl.enableDisplayMode("block");
5579 getChildContainer : function()
5581 if (this.el && this.el.select('.collapse').getCount()) {
5582 return this.el.select('.collapse',true).first();
5597 onToggle : function()
5600 if(this.fireEvent('beforetoggle', this) === false){
5603 var ce = this.el.select('.navbar-collapse',true).first();
5605 if (!ce.hasClass('show')) {
5615 * Expand the navbar pulldown
5617 expand : function ()
5620 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (ce.hasClass('collapsing')) {
5624 ce.dom.style.height = '';
5626 ce.addClass('in'); // old...
5627 ce.removeClass('collapse');
5628 ce.addClass('show');
5629 var h = ce.getHeight();
5631 ce.removeClass('show');
5632 // at this point we should be able to see it..
5633 ce.addClass('collapsing');
5635 ce.setHeight(0); // resize it ...
5636 ce.on('transitionend', function() {
5637 //Roo.log('done transition');
5638 ce.removeClass('collapsing');
5639 ce.addClass('show');
5640 ce.removeClass('collapse');
5642 ce.dom.style.height = '';
5643 }, this, { single: true} );
5645 ce.dom.scrollTop = 0;
5648 * Collapse the navbar pulldown
5650 collapse : function()
5652 var ce = this.el.select('.navbar-collapse',true).first();
5654 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5655 // it's collapsed or collapsing..
5658 ce.removeClass('in'); // old...
5659 ce.setHeight(ce.getHeight());
5660 ce.removeClass('show');
5661 ce.addClass('collapsing');
5663 ce.on('transitionend', function() {
5664 ce.dom.style.height = '';
5665 ce.removeClass('collapsing');
5666 ce.addClass('collapse');
5667 }, this, { single: true} );
5687 * @class Roo.bootstrap.NavSimplebar
5688 * @extends Roo.bootstrap.Navbar
5689 * Bootstrap Sidebar class
5691 * @cfg {Boolean} inverse is inverted color
5693 * @cfg {String} type (nav | pills | tabs)
5694 * @cfg {Boolean} arrangement stacked | justified
5695 * @cfg {String} align (left | right) alignment
5697 * @cfg {Boolean} main (true|false) main nav bar? default false
5698 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5700 * @cfg {String} tag (header|footer|nav|div) default is nav
5702 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5706 * Create a new Sidebar
5707 * @param {Object} config The config object
5711 Roo.bootstrap.NavSimplebar = function(config){
5712 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5715 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5731 getAutoCreate : function(){
5735 tag : this.tag || 'div',
5736 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5738 if (['light','white'].indexOf(this.weight) > -1) {
5739 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5741 cfg.cls += ' bg-' + this.weight;
5744 cfg.cls += ' navbar-inverse';
5748 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5750 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5759 cls: 'nav nav-' + this.xtype,
5765 this.type = this.type || 'nav';
5766 if (['tabs','pills'].indexOf(this.type) != -1) {
5767 cfg.cn[0].cls += ' nav-' + this.type
5771 if (this.type!=='nav') {
5772 Roo.log('nav type must be nav/tabs/pills')
5774 cfg.cn[0].cls += ' navbar-nav'
5780 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5781 cfg.cn[0].cls += ' nav-' + this.arrangement;
5785 if (this.align === 'right') {
5786 cfg.cn[0].cls += ' navbar-right';
5811 * navbar-expand-md fixed-top
5815 * @class Roo.bootstrap.NavHeaderbar
5816 * @extends Roo.bootstrap.NavSimplebar
5817 * Bootstrap Sidebar class
5819 * @cfg {String} brand what is brand
5820 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5821 * @cfg {String} brand_href href of the brand
5822 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5823 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5824 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5825 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5828 * Create a new Sidebar
5829 * @param {Object} config The config object
5833 Roo.bootstrap.NavHeaderbar = function(config){
5834 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5838 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5845 desktopCenter : false,
5848 getAutoCreate : function(){
5851 tag: this.nav || 'nav',
5852 cls: 'navbar navbar-expand-md',
5858 if (this.desktopCenter) {
5859 cn.push({cls : 'container', cn : []});
5867 cls: 'navbar-toggle navbar-toggler',
5868 'data-toggle': 'collapse',
5873 html: 'Toggle navigation'
5877 cls: 'icon-bar navbar-toggler-icon'
5890 cn.push( Roo.bootstrap.version == 4 ? btn : {
5892 cls: 'navbar-header',
5901 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5905 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5907 if (['light','white'].indexOf(this.weight) > -1) {
5908 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5910 cfg.cls += ' bg-' + this.weight;
5913 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5914 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5916 // tag can override this..
5918 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5921 if (this.brand !== '') {
5922 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5923 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5925 href: this.brand_href ? this.brand_href : '#',
5926 cls: 'navbar-brand',
5934 cfg.cls += ' main-nav';
5942 getHeaderChildContainer : function()
5944 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5945 return this.el.select('.navbar-header',true).first();
5948 return this.getChildContainer();
5951 getChildContainer : function()
5954 return this.el.select('.roo-navbar-collapse',true).first();
5959 initEvents : function()
5961 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5963 if (this.autohide) {
5968 Roo.get(document).on('scroll',function(e) {
5969 var ns = Roo.get(document).getScroll().top;
5970 var os = prevScroll;
5974 ft.removeClass('slideDown');
5975 ft.addClass('slideUp');
5978 ft.removeClass('slideUp');
5979 ft.addClass('slideDown');
6000 * @class Roo.bootstrap.NavSidebar
6001 * @extends Roo.bootstrap.Navbar
6002 * Bootstrap Sidebar class
6005 * Create a new Sidebar
6006 * @param {Object} config The config object
6010 Roo.bootstrap.NavSidebar = function(config){
6011 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6014 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6016 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6018 getAutoCreate : function(){
6023 cls: 'sidebar sidebar-nav'
6045 * @class Roo.bootstrap.NavGroup
6046 * @extends Roo.bootstrap.Component
6047 * Bootstrap NavGroup class
6048 * @cfg {String} align (left|right)
6049 * @cfg {Boolean} inverse
6050 * @cfg {String} type (nav|pills|tab) default nav
6051 * @cfg {String} navId - reference Id for navbar.
6052 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6055 * Create a new nav group
6056 * @param {Object} config The config object
6059 Roo.bootstrap.NavGroup = function(config){
6060 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6063 Roo.bootstrap.NavGroup.register(this);
6067 * Fires when the active item changes
6068 * @param {Roo.bootstrap.NavGroup} this
6069 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6070 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6077 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6089 getAutoCreate : function()
6091 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6097 if (Roo.bootstrap.version == 4) {
6098 if (['tabs','pills'].indexOf(this.type) != -1) {
6099 cfg.cls += ' nav-' + this.type;
6101 // trying to remove so header bar can right align top?
6102 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6103 // do not use on header bar...
6104 cfg.cls += ' navbar-nav';
6109 if (['tabs','pills'].indexOf(this.type) != -1) {
6110 cfg.cls += ' nav-' + this.type
6112 if (this.type !== 'nav') {
6113 Roo.log('nav type must be nav/tabs/pills')
6115 cfg.cls += ' navbar-nav'
6119 if (this.parent() && this.parent().sidebar) {
6122 cls: 'dashboard-menu sidebar-menu'
6128 if (this.form === true) {
6131 cls: 'navbar-form form-inline'
6133 //nav navbar-right ml-md-auto
6134 if (this.align === 'right') {
6135 cfg.cls += ' navbar-right ml-md-auto';
6137 cfg.cls += ' navbar-left';
6141 if (this.align === 'right') {
6142 cfg.cls += ' navbar-right ml-md-auto';
6144 cfg.cls += ' mr-auto';
6148 cfg.cls += ' navbar-inverse';
6156 * sets the active Navigation item
6157 * @param {Roo.bootstrap.NavItem} the new current navitem
6159 setActiveItem : function(item)
6162 Roo.each(this.navItems, function(v){
6167 v.setActive(false, true);
6174 item.setActive(true, true);
6175 this.fireEvent('changed', this, item, prev);
6180 * gets the active Navigation item
6181 * @return {Roo.bootstrap.NavItem} the current navitem
6183 getActive : function()
6187 Roo.each(this.navItems, function(v){
6198 indexOfNav : function()
6202 Roo.each(this.navItems, function(v,i){
6213 * adds a Navigation item
6214 * @param {Roo.bootstrap.NavItem} the navitem to add
6216 addItem : function(cfg)
6218 if (this.form && Roo.bootstrap.version == 4) {
6221 var cn = new Roo.bootstrap.NavItem(cfg);
6223 cn.parentId = this.id;
6224 cn.onRender(this.el, null);
6228 * register a Navigation item
6229 * @param {Roo.bootstrap.NavItem} the navitem to add
6231 register : function(item)
6233 this.navItems.push( item);
6234 item.navId = this.navId;
6239 * clear all the Navigation item
6242 clearAll : function()
6245 this.el.dom.innerHTML = '';
6248 getNavItem: function(tabId)
6251 Roo.each(this.navItems, function(e) {
6252 if (e.tabId == tabId) {
6262 setActiveNext : function()
6264 var i = this.indexOfNav(this.getActive());
6265 if (i > this.navItems.length) {
6268 this.setActiveItem(this.navItems[i+1]);
6270 setActivePrev : function()
6272 var i = this.indexOfNav(this.getActive());
6276 this.setActiveItem(this.navItems[i-1]);
6278 clearWasActive : function(except) {
6279 Roo.each(this.navItems, function(e) {
6280 if (e.tabId != except.tabId && e.was_active) {
6281 e.was_active = false;
6288 getWasActive : function ()
6291 Roo.each(this.navItems, function(e) {
6306 Roo.apply(Roo.bootstrap.NavGroup, {
6310 * register a Navigation Group
6311 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6313 register : function(navgrp)
6315 this.groups[navgrp.navId] = navgrp;
6319 * fetch a Navigation Group based on the navigation ID
6320 * @param {string} the navgroup to add
6321 * @returns {Roo.bootstrap.NavGroup} the navgroup
6323 get: function(navId) {
6324 if (typeof(this.groups[navId]) == 'undefined') {
6326 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6328 return this.groups[navId] ;
6343 * @class Roo.bootstrap.NavItem
6344 * @extends Roo.bootstrap.Component
6345 * Bootstrap Navbar.NavItem class
6346 * @cfg {String} href link to
6347 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6348 * @cfg {Boolean} button_outline show and outlined button
6349 * @cfg {String} html content of button
6350 * @cfg {String} badge text inside badge
6351 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6352 * @cfg {String} glyphicon DEPRICATED - use fa
6353 * @cfg {String} icon DEPRICATED - use fa
6354 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6355 * @cfg {Boolean} active Is item active
6356 * @cfg {Boolean} disabled Is item disabled
6357 * @cfg {String} linkcls Link Class
6358 * @cfg {Boolean} preventDefault (true | false) default false
6359 * @cfg {String} tabId the tab that this item activates.
6360 * @cfg {String} tagtype (a|span) render as a href or span?
6361 * @cfg {Boolean} animateRef (true|false) link to element default false
6364 * Create a new Navbar Item
6365 * @param {Object} config The config object
6367 Roo.bootstrap.NavItem = function(config){
6368 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6373 * The raw click event for the entire grid.
6374 * @param {Roo.EventObject} e
6379 * Fires when the active item active state changes
6380 * @param {Roo.bootstrap.NavItem} this
6381 * @param {boolean} state the new state
6387 * Fires when scroll to element
6388 * @param {Roo.bootstrap.NavItem} this
6389 * @param {Object} options
6390 * @param {Roo.EventObject} e
6398 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6407 preventDefault : false,
6415 button_outline : false,
6419 getAutoCreate : function(){
6426 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6429 cfg.cls += ' active' ;
6431 if (this.disabled) {
6432 cfg.cls += ' disabled';
6436 if (this.button_weight.length) {
6437 cfg.tag = this.href ? 'a' : 'button';
6438 cfg.html = this.html || '';
6439 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6441 cfg.href = this.href;
6444 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6446 cfg.cls += " nav-html";
6449 // menu .. should add dropdown-menu class - so no need for carat..
6451 if (this.badge !== '') {
6453 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6458 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6462 href : this.href || "#",
6463 html: this.html || '',
6467 if (this.tagtype == 'a') {
6468 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6472 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6473 } else if (this.fa) {
6474 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475 } else if(this.glyphicon) {
6476 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6478 cfg.cn[0].cls += " nav-html";
6482 cfg.cn[0].html += " <span class='caret'></span>";
6486 if (this.badge !== '') {
6487 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6495 onRender : function(ct, position)
6497 // Roo.log("Call onRender: " + this.xtype);
6498 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6502 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6503 this.navLink = this.el.select('.nav-link',true).first();
6504 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6509 initEvents: function()
6511 if (typeof (this.menu) != 'undefined') {
6512 this.menu.parentType = this.xtype;
6513 this.menu.triggerEl = this.el;
6514 this.menu = this.addxtype(Roo.apply({}, this.menu));
6517 this.el.on('click', this.onClick, this);
6519 //if(this.tagtype == 'span'){
6520 // this.el.select('span',true).on('click', this.onClick, this);
6523 // at this point parent should be available..
6524 this.parent().register(this);
6527 onClick : function(e)
6529 if (e.getTarget('.dropdown-menu-item')) {
6530 // did you click on a menu itemm.... - then don't trigger onclick..
6535 this.preventDefault ||
6538 Roo.log("NavItem - prevent Default?");
6542 if (this.disabled) {
6546 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6547 if (tg && tg.transition) {
6548 Roo.log("waiting for the transitionend");
6554 //Roo.log("fire event clicked");
6555 if(this.fireEvent('click', this, e) === false){
6559 if(this.tagtype == 'span'){
6563 //Roo.log(this.href);
6564 var ael = this.el.select('a',true).first();
6567 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6568 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6569 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6570 return; // ignore... - it's a 'hash' to another page.
6572 Roo.log("NavItem - prevent Default?");
6574 this.scrollToElement(e);
6578 var p = this.parent();
6580 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6581 if (typeof(p.setActiveItem) !== 'undefined') {
6582 p.setActiveItem(this);
6586 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6587 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6588 // remove the collapsed menu expand...
6589 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6593 isActive: function () {
6596 setActive : function(state, fire, is_was_active)
6598 if (this.active && !state && this.navId) {
6599 this.was_active = true;
6600 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6602 nv.clearWasActive(this);
6606 this.active = state;
6609 this.el.removeClass('active');
6610 this.navLink ? this.navLink.removeClass('active') : false;
6611 } else if (!this.el.hasClass('active')) {
6613 this.el.addClass('active');
6614 if (Roo.bootstrap.version == 4 && this.navLink ) {
6615 this.navLink.addClass('active');
6620 this.fireEvent('changed', this, state);
6623 // show a panel if it's registered and related..
6625 if (!this.navId || !this.tabId || !state || is_was_active) {
6629 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6633 var pan = tg.getPanelByName(this.tabId);
6637 // if we can not flip to new panel - go back to old nav highlight..
6638 if (false == tg.showPanel(pan)) {
6639 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6641 var onav = nv.getWasActive();
6643 onav.setActive(true, false, true);
6652 // this should not be here...
6653 setDisabled : function(state)
6655 this.disabled = state;
6657 this.el.removeClass('disabled');
6658 } else if (!this.el.hasClass('disabled')) {
6659 this.el.addClass('disabled');
6665 * Fetch the element to display the tooltip on.
6666 * @return {Roo.Element} defaults to this.el
6668 tooltipEl : function()
6670 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6673 scrollToElement : function(e)
6675 var c = document.body;
6678 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6680 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6681 c = document.documentElement;
6684 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6690 var o = target.calcOffsetsTo(c);
6697 this.fireEvent('scrollto', this, options, e);
6699 Roo.get(c).scrollTo('top', options.value, true);
6704 * Set the HTML (text content) of the item
6705 * @param {string} html content for the nav item
6707 setHtml : function(html)
6710 this.htmlEl.dom.innerHTML = html;
6722 * <span> icon </span>
6723 * <span> text </span>
6724 * <span>badge </span>
6728 * @class Roo.bootstrap.NavSidebarItem
6729 * @extends Roo.bootstrap.NavItem
6730 * Bootstrap Navbar.NavSidebarItem class
6731 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6732 * {Boolean} open is the menu open
6733 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6734 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6735 * {String} buttonSize (sm|md|lg)the extra classes for the button
6736 * {Boolean} showArrow show arrow next to the text (default true)
6738 * Create a new Navbar Button
6739 * @param {Object} config The config object
6741 Roo.bootstrap.NavSidebarItem = function(config){
6742 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6747 * The raw click event for the entire grid.
6748 * @param {Roo.EventObject} e
6753 * Fires when the active item active state changes
6754 * @param {Roo.bootstrap.NavSidebarItem} this
6755 * @param {boolean} state the new state
6763 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6765 badgeWeight : 'default',
6771 buttonWeight : 'default',
6777 getAutoCreate : function(){
6782 href : this.href || '#',
6788 if(this.buttonView){
6791 href : this.href || '#',
6792 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6805 cfg.cls += ' active';
6808 if (this.disabled) {
6809 cfg.cls += ' disabled';
6812 cfg.cls += ' open x-open';
6815 if (this.glyphicon || this.icon) {
6816 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6817 a.cn.push({ tag : 'i', cls : c }) ;
6820 if(!this.buttonView){
6823 html : this.html || ''
6830 if (this.badge !== '') {
6831 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6837 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6840 a.cls += ' dropdown-toggle treeview' ;
6846 initEvents : function()
6848 if (typeof (this.menu) != 'undefined') {
6849 this.menu.parentType = this.xtype;
6850 this.menu.triggerEl = this.el;
6851 this.menu = this.addxtype(Roo.apply({}, this.menu));
6854 this.el.on('click', this.onClick, this);
6856 if(this.badge !== ''){
6857 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6862 onClick : function(e)
6869 if(this.preventDefault){
6873 this.fireEvent('click', this, e);
6876 disable : function()
6878 this.setDisabled(true);
6883 this.setDisabled(false);
6886 setDisabled : function(state)
6888 if(this.disabled == state){
6892 this.disabled = state;
6895 this.el.addClass('disabled');
6899 this.el.removeClass('disabled');
6904 setActive : function(state)
6906 if(this.active == state){
6910 this.active = state;
6913 this.el.addClass('active');
6917 this.el.removeClass('active');
6922 isActive: function ()
6927 setBadge : function(str)
6933 this.badgeEl.dom.innerHTML = str;
6948 Roo.namespace('Roo.bootstrap.breadcrumb');
6952 * @class Roo.bootstrap.breadcrumb.Nav
6953 * @extends Roo.bootstrap.Component
6954 * Bootstrap Breadcrumb Nav Class
6956 * @children Roo.bootstrap.breadcrumb.Item
6959 * Create a new breadcrumb.Nav
6960 * @param {Object} config The config object
6964 Roo.bootstrap.breadcrumb.Nav = function(config){
6965 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6970 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6972 getAutoCreate : function()
6989 initEvents: function()
6991 this.olEl = this.el.select('ol',true).first();
6993 getChildContainer : function()
7009 * @class Roo.bootstrap.breadcrumb.Nav
7010 * @extends Roo.bootstrap.Component
7011 * Bootstrap Breadcrumb Nav Class
7013 * @children Roo.bootstrap.breadcrumb.Component
7014 * @cfg {String} html the content of the link.
7015 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7016 * @cfg {Boolean} active is it active
7020 * Create a new breadcrumb.Nav
7021 * @param {Object} config The config object
7024 Roo.bootstrap.breadcrumb.Item = function(config){
7025 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7030 * The img click event for the img.
7031 * @param {Roo.EventObject} e
7038 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7043 getAutoCreate : function()
7048 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7050 if (this.href !== false) {
7057 cfg.html = this.html;
7063 initEvents: function()
7066 this.el.select('a', true).first().on('click',this.onClick, this)
7070 onClick : function(e)
7073 this.fireEvent('click',this, e);
7086 * @class Roo.bootstrap.Row
7087 * @extends Roo.bootstrap.Component
7088 * Bootstrap Row class (contains columns...)
7092 * @param {Object} config The config object
7095 Roo.bootstrap.Row = function(config){
7096 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7099 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7101 getAutoCreate : function(){
7120 * @class Roo.bootstrap.Pagination
7121 * @extends Roo.bootstrap.Component
7122 * Bootstrap Pagination class
7123 * @cfg {String} size xs | sm | md | lg
7124 * @cfg {Boolean} inverse false | true
7127 * Create a new Pagination
7128 * @param {Object} config The config object
7131 Roo.bootstrap.Pagination = function(config){
7132 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7135 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7141 getAutoCreate : function(){
7147 cfg.cls += ' inverse';
7153 cfg.cls += " " + this.cls;
7171 * @class Roo.bootstrap.PaginationItem
7172 * @extends Roo.bootstrap.Component
7173 * Bootstrap PaginationItem class
7174 * @cfg {String} html text
7175 * @cfg {String} href the link
7176 * @cfg {Boolean} preventDefault (true | false) default true
7177 * @cfg {Boolean} active (true | false) default false
7178 * @cfg {Boolean} disabled default false
7182 * Create a new PaginationItem
7183 * @param {Object} config The config object
7187 Roo.bootstrap.PaginationItem = function(config){
7188 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7193 * The raw click event for the entire grid.
7194 * @param {Roo.EventObject} e
7200 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7204 preventDefault: true,
7209 getAutoCreate : function(){
7215 href : this.href ? this.href : '#',
7216 html : this.html ? this.html : ''
7226 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7230 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7236 initEvents: function() {
7238 this.el.on('click', this.onClick, this);
7241 onClick : function(e)
7243 Roo.log('PaginationItem on click ');
7244 if(this.preventDefault){
7252 this.fireEvent('click', this, e);
7268 * @class Roo.bootstrap.Slider
7269 * @extends Roo.bootstrap.Component
7270 * Bootstrap Slider class
7273 * Create a new Slider
7274 * @param {Object} config The config object
7277 Roo.bootstrap.Slider = function(config){
7278 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7281 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7283 getAutoCreate : function(){
7287 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7291 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7303 * Ext JS Library 1.1.1
7304 * Copyright(c) 2006-2007, Ext JS, LLC.
7306 * Originally Released Under LGPL - original licence link has changed is not relivant.
7309 * <script type="text/javascript">
7312 * @extends Roo.dd.DDProxy
7313 * @class Roo.grid.SplitDragZone
7314 * Support for Column Header resizing
7316 * @param {Object} config
7319 // This is a support class used internally by the Grid components
7320 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7322 this.view = grid.getView();
7323 this.proxy = this.view.resizeProxy;
7324 Roo.grid.SplitDragZone.superclass.constructor.call(
7327 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7329 dragElId : Roo.id(this.proxy.dom),
7334 this.setHandleElId(Roo.id(hd));
7335 if (hd2 !== false) {
7336 this.setOuterHandleElId(Roo.id(hd2));
7339 this.scroll = false;
7341 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7342 fly: Roo.Element.fly,
7344 b4StartDrag : function(x, y){
7345 this.view.headersDisabled = true;
7346 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7347 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7349 this.proxy.setHeight(h);
7351 // for old system colWidth really stored the actual width?
7352 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7353 // which in reality did not work.. - it worked only for fixed sizes
7354 // for resizable we need to use actual sizes.
7355 var w = this.cm.getColumnWidth(this.cellIndex);
7356 if (!this.view.mainWrap) {
7358 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7363 // this was w-this.grid.minColumnWidth;
7364 // doesnt really make sense? - w = thie curren width or the rendered one?
7365 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7366 this.resetConstraints();
7367 this.setXConstraint(minw, 1000);
7368 this.setYConstraint(0, 0);
7369 this.minX = x - minw;
7370 this.maxX = x + 1000;
7372 if (!this.view.mainWrap) { // this is Bootstrap code..
7373 this.getDragEl().style.display='block';
7376 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7380 handleMouseDown : function(e){
7381 ev = Roo.EventObject.setEvent(e);
7382 var t = this.fly(ev.getTarget());
7383 if(t.hasClass("x-grid-split")){
7384 this.cellIndex = this.view.getCellIndex(t.dom);
7386 this.cm = this.grid.colModel;
7387 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7388 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7393 endDrag : function(e){
7394 this.view.headersDisabled = false;
7395 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7396 var diff = endX - this.startPos;
7398 var w = this.cm.getColumnWidth(this.cellIndex);
7399 if (!this.view.mainWrap) {
7402 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7405 autoOffset : function(){
7410 * Ext JS Library 1.1.1
7411 * Copyright(c) 2006-2007, Ext JS, LLC.
7413 * Originally Released Under LGPL - original licence link has changed is not relivant.
7416 * <script type="text/javascript">
7420 * @class Roo.grid.AbstractSelectionModel
7421 * @extends Roo.util.Observable
7422 * Abstract base class for grid SelectionModels. It provides the interface that should be
7423 * implemented by descendant classes. This class should not be directly instantiated.
7426 Roo.grid.AbstractSelectionModel = function(){
7427 this.locked = false;
7428 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7431 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7432 /** @ignore Called by the grid automatically. Do not call directly. */
7433 init : function(grid){
7439 * Locks the selections.
7446 * Unlocks the selections.
7448 unlock : function(){
7449 this.locked = false;
7453 * Returns true if the selections are locked.
7456 isLocked : function(){
7461 * Ext JS Library 1.1.1
7462 * Copyright(c) 2006-2007, Ext JS, LLC.
7464 * Originally Released Under LGPL - original licence link has changed is not relivant.
7467 * <script type="text/javascript">
7470 * @extends Roo.grid.AbstractSelectionModel
7471 * @class Roo.grid.RowSelectionModel
7472 * The default SelectionModel used by {@link Roo.grid.Grid}.
7473 * It supports multiple selections and keyboard selection/navigation.
7475 * @param {Object} config
7477 Roo.grid.RowSelectionModel = function(config){
7478 Roo.apply(this, config);
7479 this.selections = new Roo.util.MixedCollection(false, function(o){
7484 this.lastActive = false;
7488 * @event selectionchange
7489 * Fires when the selection changes
7490 * @param {SelectionModel} this
7492 "selectionchange" : true,
7494 * @event afterselectionchange
7495 * Fires after the selection changes (eg. by key press or clicking)
7496 * @param {SelectionModel} this
7498 "afterselectionchange" : true,
7500 * @event beforerowselect
7501 * Fires when a row is selected being selected, return false to cancel.
7502 * @param {SelectionModel} this
7503 * @param {Number} rowIndex The selected index
7504 * @param {Boolean} keepExisting False if other selections will be cleared
7506 "beforerowselect" : true,
7509 * Fires when a row is selected.
7510 * @param {SelectionModel} this
7511 * @param {Number} rowIndex The selected index
7512 * @param {Roo.data.Record} r The record
7516 * @event rowdeselect
7517 * Fires when a row is deselected.
7518 * @param {SelectionModel} this
7519 * @param {Number} rowIndex The selected index
7521 "rowdeselect" : true
7523 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7524 this.locked = false;
7527 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7529 * @cfg {Boolean} singleSelect
7530 * True to allow selection of only one row at a time (defaults to false)
7532 singleSelect : false,
7535 initEvents : function(){
7537 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7538 this.grid.on("mousedown", this.handleMouseDown, this);
7539 }else{ // allow click to work like normal
7540 this.grid.on("rowclick", this.handleDragableRowClick, this);
7542 // bootstrap does not have a view..
7543 var view = this.grid.view ? this.grid.view : this.grid;
7544 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7547 this.selectPrevious(e.shiftKey);
7548 }else if(this.last !== false && this.lastActive !== false){
7549 var last = this.last;
7550 this.selectRange(this.last, this.lastActive-1);
7551 view.focusRow(this.lastActive);
7556 this.selectFirstRow();
7558 this.fireEvent("afterselectionchange", this);
7560 "down" : function(e){
7562 this.selectNext(e.shiftKey);
7563 }else if(this.last !== false && this.lastActive !== false){
7564 var last = this.last;
7565 this.selectRange(this.last, this.lastActive+1);
7566 view.focusRow(this.lastActive);
7571 this.selectFirstRow();
7573 this.fireEvent("afterselectionchange", this);
7579 view.on("refresh", this.onRefresh, this);
7580 view.on("rowupdated", this.onRowUpdated, this);
7581 view.on("rowremoved", this.onRemove, this);
7585 onRefresh : function(){
7586 var ds = this.grid.ds, i, v = this.grid.view;
7587 var s = this.selections;
7589 if((i = ds.indexOfId(r.id)) != -1){
7591 s.add(ds.getAt(i)); // updating the selection relate data
7599 onRemove : function(v, index, r){
7600 this.selections.remove(r);
7604 onRowUpdated : function(v, index, r){
7605 if(this.isSelected(r)){
7606 v.onRowSelect(index);
7612 * @param {Array} records The records to select
7613 * @param {Boolean} keepExisting (optional) True to keep existing selections
7615 selectRecords : function(records, keepExisting){
7617 this.clearSelections();
7619 var ds = this.grid.ds;
7620 for(var i = 0, len = records.length; i < len; i++){
7621 this.selectRow(ds.indexOf(records[i]), true);
7626 * Gets the number of selected rows.
7629 getCount : function(){
7630 return this.selections.length;
7634 * Selects the first row in the grid.
7636 selectFirstRow : function(){
7641 * Select the last row.
7642 * @param {Boolean} keepExisting (optional) True to keep existing selections
7644 selectLastRow : function(keepExisting){
7645 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7649 * Selects the row immediately following the last selected row.
7650 * @param {Boolean} keepExisting (optional) True to keep existing selections
7652 selectNext : function(keepExisting){
7653 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7654 this.selectRow(this.last+1, keepExisting);
7655 var view = this.grid.view ? this.grid.view : this.grid;
7656 view.focusRow(this.last);
7661 * Selects the row that precedes the last selected row.
7662 * @param {Boolean} keepExisting (optional) True to keep existing selections
7664 selectPrevious : function(keepExisting){
7666 this.selectRow(this.last-1, keepExisting);
7667 var view = this.grid.view ? this.grid.view : this.grid;
7668 view.focusRow(this.last);
7673 * Returns the selected records
7674 * @return {Array} Array of selected records
7676 getSelections : function(){
7677 return [].concat(this.selections.items);
7681 * Returns the first selected record.
7684 getSelected : function(){
7685 return this.selections.itemAt(0);
7690 * Clears all selections.
7692 clearSelections : function(fast){
7697 var ds = this.grid.ds;
7698 var s = this.selections;
7700 this.deselectRow(ds.indexOfId(r.id));
7704 this.selections.clear();
7713 selectAll : function(){
7717 this.selections.clear();
7718 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7719 this.selectRow(i, true);
7724 * Returns True if there is a selection.
7727 hasSelection : function(){
7728 return this.selections.length > 0;
7732 * Returns True if the specified row is selected.
7733 * @param {Number/Record} record The record or index of the record to check
7736 isSelected : function(index){
7737 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7738 return (r && this.selections.key(r.id) ? true : false);
7742 * Returns True if the specified record id is selected.
7743 * @param {String} id The id of record to check
7746 isIdSelected : function(id){
7747 return (this.selections.key(id) ? true : false);
7751 handleMouseDown : function(e, t)
7753 var view = this.grid.view ? this.grid.view : this.grid;
7755 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7758 if(e.shiftKey && this.last !== false){
7759 var last = this.last;
7760 this.selectRange(last, rowIndex, e.ctrlKey);
7761 this.last = last; // reset the last
7762 view.focusRow(rowIndex);
7764 var isSelected = this.isSelected(rowIndex);
7765 if(e.button !== 0 && isSelected){
7766 view.focusRow(rowIndex);
7767 }else if(e.ctrlKey && isSelected){
7768 this.deselectRow(rowIndex);
7769 }else if(!isSelected){
7770 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7771 view.focusRow(rowIndex);
7774 this.fireEvent("afterselectionchange", this);
7777 handleDragableRowClick : function(grid, rowIndex, e)
7779 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7780 this.selectRow(rowIndex, false);
7781 var view = this.grid.view ? this.grid.view : this.grid;
7782 view.focusRow(rowIndex);
7783 this.fireEvent("afterselectionchange", this);
7788 * Selects multiple rows.
7789 * @param {Array} rows Array of the indexes of the row to select
7790 * @param {Boolean} keepExisting (optional) True to keep existing selections
7792 selectRows : function(rows, keepExisting){
7794 this.clearSelections();
7796 for(var i = 0, len = rows.length; i < len; i++){
7797 this.selectRow(rows[i], true);
7802 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7803 * @param {Number} startRow The index of the first row in the range
7804 * @param {Number} endRow The index of the last row in the range
7805 * @param {Boolean} keepExisting (optional) True to retain existing selections
7807 selectRange : function(startRow, endRow, keepExisting){
7812 this.clearSelections();
7814 if(startRow <= endRow){
7815 for(var i = startRow; i <= endRow; i++){
7816 this.selectRow(i, true);
7819 for(var i = startRow; i >= endRow; i--){
7820 this.selectRow(i, true);
7826 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7827 * @param {Number} startRow The index of the first row in the range
7828 * @param {Number} endRow The index of the last row in the range
7830 deselectRange : function(startRow, endRow, preventViewNotify){
7834 for(var i = startRow; i <= endRow; i++){
7835 this.deselectRow(i, preventViewNotify);
7841 * @param {Number} row The index of the row to select
7842 * @param {Boolean} keepExisting (optional) True to keep existing selections
7844 selectRow : function(index, keepExisting, preventViewNotify){
7845 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7848 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7849 if(!keepExisting || this.singleSelect){
7850 this.clearSelections();
7852 var r = this.grid.ds.getAt(index);
7853 this.selections.add(r);
7854 this.last = this.lastActive = index;
7855 if(!preventViewNotify){
7856 var view = this.grid.view ? this.grid.view : this.grid;
7857 view.onRowSelect(index);
7859 this.fireEvent("rowselect", this, index, r);
7860 this.fireEvent("selectionchange", this);
7866 * @param {Number} row The index of the row to deselect
7868 deselectRow : function(index, preventViewNotify){
7872 if(this.last == index){
7875 if(this.lastActive == index){
7876 this.lastActive = false;
7878 var r = this.grid.ds.getAt(index);
7879 this.selections.remove(r);
7880 if(!preventViewNotify){
7881 var view = this.grid.view ? this.grid.view : this.grid;
7882 view.onRowDeselect(index);
7884 this.fireEvent("rowdeselect", this, index);
7885 this.fireEvent("selectionchange", this);
7889 restoreLast : function(){
7891 this.last = this._last;
7896 acceptsNav : function(row, col, cm){
7897 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7901 onEditorKey : function(field, e){
7902 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7907 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7909 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7911 }else if(k == e.ENTER && !e.ctrlKey){
7915 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7917 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7919 }else if(k == e.ESC){
7923 g.startEditing(newCell[0], newCell[1]);
7928 * Ext JS Library 1.1.1
7929 * Copyright(c) 2006-2007, Ext JS, LLC.
7931 * Originally Released Under LGPL - original licence link has changed is not relivant.
7934 * <script type="text/javascript">
7939 * @class Roo.grid.ColumnModel
7940 * @extends Roo.util.Observable
7941 * This is the default implementation of a ColumnModel used by the Grid. It defines
7942 * the columns in the grid.
7945 var colModel = new Roo.grid.ColumnModel([
7946 {header: "Ticker", width: 60, sortable: true, locked: true},
7947 {header: "Company Name", width: 150, sortable: true},
7948 {header: "Market Cap.", width: 100, sortable: true},
7949 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7950 {header: "Employees", width: 100, sortable: true, resizable: false}
7955 * The config options listed for this class are options which may appear in each
7956 * individual column definition.
7957 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7959 * @param {Object} config An Array of column config objects. See this class's
7960 * config objects for details.
7962 Roo.grid.ColumnModel = function(config){
7964 * The config passed into the constructor
7966 this.config = []; //config;
7969 // if no id, create one
7970 // if the column does not have a dataIndex mapping,
7971 // map it to the order it is in the config
7972 for(var i = 0, len = config.length; i < len; i++){
7973 this.addColumn(config[i]);
7978 * The width of columns which have no width specified (defaults to 100)
7981 this.defaultWidth = 100;
7984 * Default sortable of columns which have no sortable specified (defaults to false)
7987 this.defaultSortable = false;
7991 * @event widthchange
7992 * Fires when the width of a column changes.
7993 * @param {ColumnModel} this
7994 * @param {Number} columnIndex The column index
7995 * @param {Number} newWidth The new width
7997 "widthchange": true,
7999 * @event headerchange
8000 * Fires when the text of a header changes.
8001 * @param {ColumnModel} this
8002 * @param {Number} columnIndex The column index
8003 * @param {Number} newText The new header text
8005 "headerchange": true,
8007 * @event hiddenchange
8008 * Fires when a column is hidden or "unhidden".
8009 * @param {ColumnModel} this
8010 * @param {Number} columnIndex The column index
8011 * @param {Boolean} hidden true if hidden, false otherwise
8013 "hiddenchange": true,
8015 * @event columnmoved
8016 * Fires when a column is moved.
8017 * @param {ColumnModel} this
8018 * @param {Number} oldIndex
8019 * @param {Number} newIndex
8021 "columnmoved" : true,
8023 * @event columlockchange
8024 * Fires when a column's locked state is changed
8025 * @param {ColumnModel} this
8026 * @param {Number} colIndex
8027 * @param {Boolean} locked true if locked
8029 "columnlockchange" : true
8031 Roo.grid.ColumnModel.superclass.constructor.call(this);
8033 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8035 * @cfg {String} header The header text to display in the Grid view.
8038 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8041 * @cfg {String} smHeader Header at Bootsrap Small width
8044 * @cfg {String} mdHeader Header at Bootsrap Medium width
8047 * @cfg {String} lgHeader Header at Bootsrap Large width
8050 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8053 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8054 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8055 * specified, the column's index is used as an index into the Record's data Array.
8058 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8059 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8062 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8063 * Defaults to the value of the {@link #defaultSortable} property.
8064 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8067 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8070 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8073 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8076 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8079 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8080 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8081 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8082 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8085 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8088 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8091 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8094 * @cfg {String} cursor (Optional)
8097 * @cfg {String} tooltip (Optional)
8100 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8103 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8106 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8109 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8112 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8115 * Returns the id of the column at the specified index.
8116 * @param {Number} index The column index
8117 * @return {String} the id
8119 getColumnId : function(index){
8120 return this.config[index].id;
8124 * Returns the column for a specified id.
8125 * @param {String} id The column id
8126 * @return {Object} the column
8128 getColumnById : function(id){
8129 return this.lookup[id];
8134 * Returns the column Object for a specified dataIndex.
8135 * @param {String} dataIndex The column dataIndex
8136 * @return {Object|Boolean} the column or false if not found
8138 getColumnByDataIndex: function(dataIndex){
8139 var index = this.findColumnIndex(dataIndex);
8140 return index > -1 ? this.config[index] : false;
8144 * Returns the index for a specified column id.
8145 * @param {String} id The column id
8146 * @return {Number} the index, or -1 if not found
8148 getIndexById : function(id){
8149 for(var i = 0, len = this.config.length; i < len; i++){
8150 if(this.config[i].id == id){
8158 * Returns the index for a specified column dataIndex.
8159 * @param {String} dataIndex The column dataIndex
8160 * @return {Number} the index, or -1 if not found
8163 findColumnIndex : function(dataIndex){
8164 for(var i = 0, len = this.config.length; i < len; i++){
8165 if(this.config[i].dataIndex == dataIndex){
8173 moveColumn : function(oldIndex, newIndex){
8174 var c = this.config[oldIndex];
8175 this.config.splice(oldIndex, 1);
8176 this.config.splice(newIndex, 0, c);
8177 this.dataMap = null;
8178 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8181 isLocked : function(colIndex){
8182 return this.config[colIndex].locked === true;
8185 setLocked : function(colIndex, value, suppressEvent){
8186 if(this.isLocked(colIndex) == value){
8189 this.config[colIndex].locked = value;
8191 this.fireEvent("columnlockchange", this, colIndex, value);
8195 getTotalLockedWidth : function(){
8197 for(var i = 0; i < this.config.length; i++){
8198 if(this.isLocked(i) && !this.isHidden(i)){
8199 this.totalWidth += this.getColumnWidth(i);
8205 getLockedCount : function(){
8206 for(var i = 0, len = this.config.length; i < len; i++){
8207 if(!this.isLocked(i)){
8212 return this.config.length;
8216 * Returns the number of columns.
8219 getColumnCount : function(visibleOnly){
8220 if(visibleOnly === true){
8222 for(var i = 0, len = this.config.length; i < len; i++){
8223 if(!this.isHidden(i)){
8229 return this.config.length;
8233 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8234 * @param {Function} fn
8235 * @param {Object} scope (optional)
8236 * @return {Array} result
8238 getColumnsBy : function(fn, scope){
8240 for(var i = 0, len = this.config.length; i < len; i++){
8241 var c = this.config[i];
8242 if(fn.call(scope||this, c, i) === true){
8250 * Returns true if the specified column is sortable.
8251 * @param {Number} col The column index
8254 isSortable : function(col){
8255 if(typeof this.config[col].sortable == "undefined"){
8256 return this.defaultSortable;
8258 return this.config[col].sortable;
8262 * Returns the rendering (formatting) function defined for the column.
8263 * @param {Number} col The column index.
8264 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8266 getRenderer : function(col){
8267 if(!this.config[col].renderer){
8268 return Roo.grid.ColumnModel.defaultRenderer;
8270 return this.config[col].renderer;
8274 * Sets the rendering (formatting) function for a column.
8275 * @param {Number} col The column index
8276 * @param {Function} fn The function to use to process the cell's raw data
8277 * to return HTML markup for the grid view. The render function is called with
8278 * the following parameters:<ul>
8279 * <li>Data value.</li>
8280 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8281 * <li>css A CSS style string to apply to the table cell.</li>
8282 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8283 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8284 * <li>Row index</li>
8285 * <li>Column index</li>
8286 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8288 setRenderer : function(col, fn){
8289 this.config[col].renderer = fn;
8293 * Returns the width for the specified column.
8294 * @param {Number} col The column index
8295 * @param (optional) {String} gridSize bootstrap width size.
8298 getColumnWidth : function(col, gridSize)
8300 var cfg = this.config[col];
8302 if (typeof(gridSize) == 'undefined') {
8303 return cfg.width * 1 || this.defaultWidth;
8305 if (gridSize === false) { // if we set it..
8306 return cfg.width || false;
8308 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8310 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8311 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8314 return cfg[ sizes[i] ];
8321 * Sets the width for a column.
8322 * @param {Number} col The column index
8323 * @param {Number} width The new width
8325 setColumnWidth : function(col, width, suppressEvent){
8326 this.config[col].width = width;
8327 this.totalWidth = null;
8329 this.fireEvent("widthchange", this, col, width);
8334 * Returns the total width of all columns.
8335 * @param {Boolean} includeHidden True to include hidden column widths
8338 getTotalWidth : function(includeHidden){
8339 if(!this.totalWidth){
8340 this.totalWidth = 0;
8341 for(var i = 0, len = this.config.length; i < len; i++){
8342 if(includeHidden || !this.isHidden(i)){
8343 this.totalWidth += this.getColumnWidth(i);
8347 return this.totalWidth;
8351 * Returns the header for the specified column.
8352 * @param {Number} col The column index
8355 getColumnHeader : function(col){
8356 return this.config[col].header;
8360 * Sets the header for a column.
8361 * @param {Number} col The column index
8362 * @param {String} header The new header
8364 setColumnHeader : function(col, header){
8365 this.config[col].header = header;
8366 this.fireEvent("headerchange", this, col, header);
8370 * Returns the tooltip for the specified column.
8371 * @param {Number} col The column index
8374 getColumnTooltip : function(col){
8375 return this.config[col].tooltip;
8378 * Sets the tooltip for a column.
8379 * @param {Number} col The column index
8380 * @param {String} tooltip The new tooltip
8382 setColumnTooltip : function(col, tooltip){
8383 this.config[col].tooltip = tooltip;
8387 * Returns the dataIndex for the specified column.
8388 * @param {Number} col The column index
8391 getDataIndex : function(col){
8392 return this.config[col].dataIndex;
8396 * Sets the dataIndex for a column.
8397 * @param {Number} col The column index
8398 * @param {Number} dataIndex The new dataIndex
8400 setDataIndex : function(col, dataIndex){
8401 this.config[col].dataIndex = dataIndex;
8407 * Returns true if the cell is editable.
8408 * @param {Number} colIndex The column index
8409 * @param {Number} rowIndex The row index - this is nto actually used..?
8412 isCellEditable : function(colIndex, rowIndex){
8413 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8417 * Returns the editor defined for the cell/column.
8418 * return false or null to disable editing.
8419 * @param {Number} colIndex The column index
8420 * @param {Number} rowIndex The row index
8423 getCellEditor : function(colIndex, rowIndex){
8424 return this.config[colIndex].editor;
8428 * Sets if a column is editable.
8429 * @param {Number} col The column index
8430 * @param {Boolean} editable True if the column is editable
8432 setEditable : function(col, editable){
8433 this.config[col].editable = editable;
8438 * Returns true if the column is hidden.
8439 * @param {Number} colIndex The column index
8442 isHidden : function(colIndex){
8443 return this.config[colIndex].hidden;
8448 * Returns true if the column width cannot be changed
8450 isFixed : function(colIndex){
8451 return this.config[colIndex].fixed;
8455 * Returns true if the column can be resized
8458 isResizable : function(colIndex){
8459 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8462 * Sets if a column is hidden.
8463 * @param {Number} colIndex The column index
8464 * @param {Boolean} hidden True if the column is hidden
8466 setHidden : function(colIndex, hidden){
8467 this.config[colIndex].hidden = hidden;
8468 this.totalWidth = null;
8469 this.fireEvent("hiddenchange", this, colIndex, hidden);
8473 * Sets the editor for a column.
8474 * @param {Number} col The column index
8475 * @param {Object} editor The editor object
8477 setEditor : function(col, editor){
8478 this.config[col].editor = editor;
8481 * Add a column (experimental...) - defaults to adding to the end..
8482 * @param {Object} config
8484 addColumn : function(c)
8487 var i = this.config.length;
8490 if(typeof c.dataIndex == "undefined"){
8493 if(typeof c.renderer == "string"){
8494 c.renderer = Roo.util.Format[c.renderer];
8496 if(typeof c.id == "undefined"){
8499 if(c.editor && c.editor.xtype){
8500 c.editor = Roo.factory(c.editor, Roo.grid);
8502 if(c.editor && c.editor.isFormField){
8503 c.editor = new Roo.grid.GridEditor(c.editor);
8505 this.lookup[c.id] = c;
8510 Roo.grid.ColumnModel.defaultRenderer = function(value)
8512 if(typeof value == "object") {
8515 if(typeof value == "string" && value.length < 1){
8519 return String.format("{0}", value);
8522 // Alias for backwards compatibility
8523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8526 * Ext JS Library 1.1.1
8527 * Copyright(c) 2006-2007, Ext JS, LLC.
8529 * Originally Released Under LGPL - original licence link has changed is not relivant.
8532 * <script type="text/javascript">
8536 * @class Roo.LoadMask
8537 * A simple utility class for generically masking elements while loading data. If the element being masked has
8538 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8539 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8540 * element's UpdateManager load indicator and will be destroyed after the initial load.
8542 * Create a new LoadMask
8543 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8544 * @param {Object} config The config object
8546 Roo.LoadMask = function(el, config){
8547 this.el = Roo.get(el);
8548 Roo.apply(this, config);
8550 this.store.on('beforeload', this.onBeforeLoad, this);
8551 this.store.on('load', this.onLoad, this);
8552 this.store.on('loadexception', this.onLoadException, this);
8553 this.removeMask = false;
8555 var um = this.el.getUpdateManager();
8556 um.showLoadIndicator = false; // disable the default indicator
8557 um.on('beforeupdate', this.onBeforeLoad, this);
8558 um.on('update', this.onLoad, this);
8559 um.on('failure', this.onLoad, this);
8560 this.removeMask = true;
8564 Roo.LoadMask.prototype = {
8566 * @cfg {Boolean} removeMask
8567 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8568 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8573 * The text to display in a centered loading message box (defaults to 'Loading...')
8577 * @cfg {String} msgCls
8578 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8580 msgCls : 'x-mask-loading',
8583 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8589 * Disables the mask to prevent it from being displayed
8591 disable : function(){
8592 this.disabled = true;
8596 * Enables the mask so that it can be displayed
8598 enable : function(){
8599 this.disabled = false;
8602 onLoadException : function()
8606 if (typeof(arguments[3]) != 'undefined') {
8607 Roo.MessageBox.alert("Error loading",arguments[3]);
8611 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8612 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8619 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8624 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8628 onBeforeLoad : function(){
8630 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8635 destroy : function(){
8637 this.store.un('beforeload', this.onBeforeLoad, this);
8638 this.store.un('load', this.onLoad, this);
8639 this.store.un('loadexception', this.onLoadException, this);
8641 var um = this.el.getUpdateManager();
8642 um.un('beforeupdate', this.onBeforeLoad, this);
8643 um.un('update', this.onLoad, this);
8644 um.un('failure', this.onLoad, this);
8648 * @class Roo.bootstrap.Table
8650 * @extends Roo.bootstrap.Component
8651 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8652 * Similar to Roo.grid.Grid
8654 var table = Roo.factory({
8656 xns : Roo.bootstrap,
8657 autoSizeColumns: true,
8664 sortInfo : { direction : 'ASC', field: 'name' },
8666 xtype : 'HttpProxy',
8669 url : 'https://example.com/some.data.url.json'
8672 xtype : 'JsonReader',
8674 fields : [ 'id', 'name', whatever' ],
8681 xtype : 'ColumnModel',
8685 dataIndex : 'is_in_group',
8688 renderer : function(v, x , r) {
8690 return String.format("{0}", v)
8696 xtype : 'RowSelectionModel',
8697 xns : Roo.bootstrap.Table
8698 // you can add listeners to catch selection change here....
8704 grid.render(Roo.get("some-div"));
8707 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8712 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8713 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8714 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8716 * @cfg {String} cls table class
8719 * @cfg {boolean} striped Should the rows be alternative striped
8720 * @cfg {boolean} bordered Add borders to the table
8721 * @cfg {boolean} hover Add hover highlighting
8722 * @cfg {boolean} condensed Format condensed
8723 * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8724 * also adds table-responsive (see bootstrap docs for details)
8725 * @cfg {Boolean} loadMask (true|false) default false
8726 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8727 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8728 * @cfg {Boolean} rowSelection (true|false) default false
8729 * @cfg {Boolean} cellSelection (true|false) default false
8730 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8731 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8732 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8733 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8734 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8735 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8738 * Create a new Table
8739 * @param {Object} config The config object
8742 Roo.bootstrap.Table = function(config)
8744 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8747 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8748 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8749 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8750 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8752 this.view = this; // compat with grid.
8754 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8756 this.sm.grid = this;
8757 this.selModel = Roo.factory(this.sm, Roo.grid);
8758 this.sm = this.selModel;
8759 this.sm.xmodule = this.xmodule || false;
8762 if (this.cm && typeof(this.cm.config) == 'undefined') {
8763 this.colModel = new Roo.grid.ColumnModel(this.cm);
8764 this.cm = this.colModel;
8765 this.cm.xmodule = this.xmodule || false;
8768 this.store= Roo.factory(this.store, Roo.data);
8769 this.ds = this.store;
8770 this.ds.xmodule = this.xmodule || false;
8773 if (this.footer && this.store) {
8774 this.footer.dataSource = this.ds;
8775 this.footer = Roo.factory(this.footer);
8782 * Fires when a cell is clicked
8783 * @param {Roo.bootstrap.Table} this
8784 * @param {Roo.Element} el
8785 * @param {Number} rowIndex
8786 * @param {Number} columnIndex
8787 * @param {Roo.EventObject} e
8791 * @event celldblclick
8792 * Fires when a cell is double clicked
8793 * @param {Roo.bootstrap.Table} this
8794 * @param {Roo.Element} el
8795 * @param {Number} rowIndex
8796 * @param {Number} columnIndex
8797 * @param {Roo.EventObject} e
8799 "celldblclick" : true,
8802 * Fires when a row is clicked
8803 * @param {Roo.bootstrap.Table} this
8804 * @param {Roo.Element} el
8805 * @param {Number} rowIndex
8806 * @param {Roo.EventObject} e
8810 * @event rowdblclick
8811 * Fires when a row is double clicked
8812 * @param {Roo.bootstrap.Table} this
8813 * @param {Roo.Element} el
8814 * @param {Number} rowIndex
8815 * @param {Roo.EventObject} e
8817 "rowdblclick" : true,
8820 * Fires when a mouseover occur
8821 * @param {Roo.bootstrap.Table} this
8822 * @param {Roo.Element} el
8823 * @param {Number} rowIndex
8824 * @param {Number} columnIndex
8825 * @param {Roo.EventObject} e
8830 * Fires when a mouseout occur
8831 * @param {Roo.bootstrap.Table} this
8832 * @param {Roo.Element} el
8833 * @param {Number} rowIndex
8834 * @param {Number} columnIndex
8835 * @param {Roo.EventObject} e
8840 * Fires when a row is rendered, so you can change add a style to it.
8841 * @param {Roo.bootstrap.Table} this
8842 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8846 * @event rowsrendered
8847 * Fires when all the rows have been rendered
8848 * @param {Roo.bootstrap.Table} this
8850 'rowsrendered' : true,
8852 * @event contextmenu
8853 * The raw contextmenu event for the entire grid.
8854 * @param {Roo.EventObject} e
8856 "contextmenu" : true,
8858 * @event rowcontextmenu
8859 * Fires when a row is right clicked
8860 * @param {Roo.bootstrap.Table} this
8861 * @param {Number} rowIndex
8862 * @param {Roo.EventObject} e
8864 "rowcontextmenu" : true,
8866 * @event cellcontextmenu
8867 * Fires when a cell is right clicked
8868 * @param {Roo.bootstrap.Table} this
8869 * @param {Number} rowIndex
8870 * @param {Number} cellIndex
8871 * @param {Roo.EventObject} e
8873 "cellcontextmenu" : true,
8875 * @event headercontextmenu
8876 * Fires when a header is right clicked
8877 * @param {Roo.bootstrap.Table} this
8878 * @param {Number} columnIndex
8879 * @param {Roo.EventObject} e
8881 "headercontextmenu" : true,
8884 * The raw mousedown event for the entire grid.
8885 * @param {Roo.EventObject} e
8892 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8908 enableColumnResize: true,
8910 rowSelection : false,
8911 cellSelection : false,
8914 minColumnWidth : 50,
8916 // Roo.Element - the tbody
8917 bodyEl: false, // <tbody> Roo.Element - thead element
8918 headEl: false, // <thead> Roo.Element - thead element
8919 resizeProxy : false, // proxy element for dragging?
8923 container: false, // used by gridpanel...
8929 auto_hide_footer : false,
8931 view: false, // actually points to this..
8933 getAutoCreate : function()
8935 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8942 // this get's auto added by panel.Grid
8943 if (this.scrollBody) {
8944 cfg.cls += ' table-body-fixed';
8947 cfg.cls += ' table-striped';
8951 cfg.cls += ' table-hover';
8953 if (this.bordered) {
8954 cfg.cls += ' table-bordered';
8956 if (this.condensed) {
8957 cfg.cls += ' table-condensed';
8960 if (this.responsive) {
8961 cfg.cls += ' table-responsive';
8965 cfg.cls+= ' ' +this.cls;
8971 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8974 if(this.store || this.cm){
8975 if(this.headerShow){
8976 cfg.cn.push(this.renderHeader());
8979 cfg.cn.push(this.renderBody());
8981 if(this.footerShow){
8982 cfg.cn.push(this.renderFooter());
8984 // where does this come from?
8985 //cfg.cls+= ' TableGrid';
8988 return { cn : [ cfg ] };
8991 initEvents : function()
8993 if(!this.store || !this.cm){
8996 if (this.selModel) {
8997 this.selModel.initEvents();
9001 //Roo.log('initEvents with ds!!!!');
9003 this.bodyEl = this.el.select('tbody', true).first();
9004 this.headEl = this.el.select('thead', true).first();
9005 this.mainFoot = this.el.select('tfoot', true).first();
9010 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9011 e.on('click', this.sort, this);
9015 // why is this done????? = it breaks dialogs??
9016 //this.parent().el.setStyle('position', 'relative');
9020 this.footer.parentId = this.id;
9021 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9024 this.el.select('tfoot tr td').first().addClass('hide');
9029 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9032 this.store.on('load', this.onLoad, this);
9033 this.store.on('beforeload', this.onBeforeLoad, this);
9034 this.store.on('update', this.onUpdate, this);
9035 this.store.on('add', this.onAdd, this);
9036 this.store.on("clear", this.clear, this);
9038 this.el.on("contextmenu", this.onContextMenu, this);
9041 this.cm.on("headerchange", this.onHeaderChange, this);
9042 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9044 //?? does bodyEl get replaced on render?
9045 this.bodyEl.on("click", this.onClick, this);
9046 this.bodyEl.on("dblclick", this.onDblClick, this);
9047 this.bodyEl.on('scroll', this.onBodyScroll, this);
9049 // guessing mainbody will work - this relays usually caught by selmodel at present.
9050 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9053 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9056 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9057 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9062 // Compatibility with grid - we implement all the view features at present.
9063 getView : function()
9068 initCSS : function()
9072 var cm = this.cm, styles = [];
9073 this.CSS.removeStyleSheet(this.id + '-cssrules');
9074 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9075 // we can honour xs/sm/md/xl as widths...
9076 // we first have to decide what widht we are currently at...
9077 var sz = Roo.getGridSize();
9081 var cols = []; // visable cols.
9083 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9084 var w = cm.getColumnWidth(i, false);
9086 cols.push( { rel : false, abs : 0 });
9090 cols.push( { rel : false, abs : w });
9092 last = i; // not really..
9095 var w = cm.getColumnWidth(i, sz);
9100 cols.push( { rel : w, abs : false });
9103 var avail = this.bodyEl.dom.clientWidth - total_abs;
9105 var unitWidth = Math.floor(avail / total);
9106 var rem = avail - (unitWidth * total);
9108 var hidden, width, pos = 0 , splithide , left;
9109 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9111 hidden = 'display:none;';
9113 width = 'width:0px;';
9115 if(!cm.isHidden(i)){
9119 // we can honour xs/sm/md/xl ?
9120 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9122 hidden = 'display:none;';
9124 // width should return a small number...
9126 w+=rem; // add the remaining with..
9129 left = "left:" + (pos -4) + "px;";
9130 width = "width:" + w+ "px;";
9133 if (this.responsive) {
9136 hidden = cm.isHidden(i) ? 'display:none;' : '';
9137 splithide = 'display: none;';
9140 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9143 splithide = 'display:none;';
9146 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9147 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9152 //Roo.log(styles.join(''));
9153 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9159 onContextMenu : function(e, t)
9161 this.processEvent("contextmenu", e);
9164 processEvent : function(name, e)
9166 if (name != 'touchstart' ) {
9167 this.fireEvent(name, e);
9170 var t = e.getTarget();
9172 var cell = Roo.get(t);
9178 if(cell.findParent('tfoot', false, true)){
9182 if(cell.findParent('thead', false, true)){
9184 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9185 cell = Roo.get(t).findParent('th', false, true);
9187 Roo.log("failed to find th in thead?");
9188 Roo.log(e.getTarget());
9193 var cellIndex = cell.dom.cellIndex;
9195 var ename = name == 'touchstart' ? 'click' : name;
9196 this.fireEvent("header" + ename, this, cellIndex, e);
9201 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9202 cell = Roo.get(t).findParent('td', false, true);
9204 Roo.log("failed to find th in tbody?");
9205 Roo.log(e.getTarget());
9210 var row = cell.findParent('tr', false, true);
9211 var cellIndex = cell.dom.cellIndex;
9212 var rowIndex = row.dom.rowIndex - 1;
9216 this.fireEvent("row" + name, this, rowIndex, e);
9220 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9226 onMouseover : function(e, el)
9228 var cell = Roo.get(el);
9234 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9235 cell = cell.findParent('td', false, true);
9238 var row = cell.findParent('tr', false, true);
9239 var cellIndex = cell.dom.cellIndex;
9240 var rowIndex = row.dom.rowIndex - 1; // start from 0
9242 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9246 onMouseout : function(e, el)
9248 var cell = Roo.get(el);
9254 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9255 cell = cell.findParent('td', false, true);
9258 var row = cell.findParent('tr', false, true);
9259 var cellIndex = cell.dom.cellIndex;
9260 var rowIndex = row.dom.rowIndex - 1; // start from 0
9262 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9266 onClick : function(e, el)
9268 var cell = Roo.get(el);
9270 if(!cell || (!this.cellSelection && !this.rowSelection)){
9274 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9275 cell = cell.findParent('td', false, true);
9278 if(!cell || typeof(cell) == 'undefined'){
9282 var row = cell.findParent('tr', false, true);
9284 if(!row || typeof(row) == 'undefined'){
9288 var cellIndex = cell.dom.cellIndex;
9289 var rowIndex = this.getRowIndex(row);
9291 // why??? - should these not be based on SelectionModel?
9292 //if(this.cellSelection){
9293 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9296 //if(this.rowSelection){
9297 this.fireEvent('rowclick', this, row, rowIndex, e);
9302 onDblClick : function(e,el)
9304 var cell = Roo.get(el);
9306 if(!cell || (!this.cellSelection && !this.rowSelection)){
9310 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9311 cell = cell.findParent('td', false, true);
9314 if(!cell || typeof(cell) == 'undefined'){
9318 var row = cell.findParent('tr', false, true);
9320 if(!row || typeof(row) == 'undefined'){
9324 var cellIndex = cell.dom.cellIndex;
9325 var rowIndex = this.getRowIndex(row);
9327 if(this.cellSelection){
9328 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9331 if(this.rowSelection){
9332 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9335 findRowIndex : function(el)
9337 var cell = Roo.get(el);
9341 var row = cell.findParent('tr', false, true);
9343 if(!row || typeof(row) == 'undefined'){
9346 return this.getRowIndex(row);
9348 sort : function(e,el)
9350 var col = Roo.get(el);
9352 if(!col.hasClass('sortable')){
9356 var sort = col.attr('sort');
9359 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9363 this.store.sortInfo = {field : sort, direction : dir};
9366 Roo.log("calling footer first");
9367 this.footer.onClick('first');
9370 this.store.load({ params : { start : 0 } });
9374 renderHeader : function()
9382 this.totalWidth = 0;
9384 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9386 var config = cm.config[i];
9390 cls : 'x-hcol-' + i,
9393 html: cm.getColumnHeader(i)
9396 var tooltip = cm.getColumnTooltip(i);
9398 c.tooltip = tooltip;
9404 if(typeof(config.sortable) != 'undefined' && config.sortable){
9405 c.cls += ' sortable';
9406 c.html = '<i class="fa"></i>' + c.html;
9409 // could use BS4 hidden-..-down
9411 if(typeof(config.lgHeader) != 'undefined'){
9412 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9415 if(typeof(config.mdHeader) != 'undefined'){
9416 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9419 if(typeof(config.smHeader) != 'undefined'){
9420 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9423 if(typeof(config.xsHeader) != 'undefined'){
9424 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9431 if(typeof(config.tooltip) != 'undefined'){
9432 c.tooltip = config.tooltip;
9435 if(typeof(config.colspan) != 'undefined'){
9436 c.colspan = config.colspan;
9439 // hidden is handled by CSS now
9441 if(typeof(config.dataIndex) != 'undefined'){
9442 c.sort = config.dataIndex;
9447 if(typeof(config.align) != 'undefined' && config.align.length){
9448 c.style += ' text-align:' + config.align + ';';
9451 /* width is done in CSS
9452 *if(typeof(config.width) != 'undefined'){
9453 c.style += ' width:' + config.width + 'px;';
9454 this.totalWidth += config.width;
9456 this.totalWidth += 100; // assume minimum of 100 per column?
9460 if(typeof(config.cls) != 'undefined'){
9461 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9463 // this is the bit that doesnt reall work at all...
9465 if (this.responsive) {
9468 ['xs','sm','md','lg'].map(function(size){
9470 if(typeof(config[size]) == 'undefined'){
9474 if (!config[size]) { // 0 = hidden
9475 // BS 4 '0' is treated as hide that column and below.
9476 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9480 c.cls += ' col-' + size + '-' + config[size] + (
9481 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9489 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9500 renderBody : function()
9510 colspan : this.cm.getColumnCount()
9520 renderFooter : function()
9530 colspan : this.cm.getColumnCount()
9544 // Roo.log('ds onload');
9549 var ds = this.store;
9551 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9552 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9553 if (_this.store.sortInfo) {
9555 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9556 e.select('i', true).addClass(['fa-arrow-up']);
9559 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9560 e.select('i', true).addClass(['fa-arrow-down']);
9565 var tbody = this.bodyEl;
9567 if(ds.getCount() > 0){
9568 ds.data.each(function(d,rowIndex){
9569 var row = this.renderRow(cm, ds, rowIndex);
9571 tbody.createChild(row);
9575 if(row.cellObjects.length){
9576 Roo.each(row.cellObjects, function(r){
9577 _this.renderCellObject(r);
9584 var tfoot = this.el.select('tfoot', true).first();
9586 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9588 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9590 var total = this.ds.getTotalCount();
9592 if(this.footer.pageSize < total){
9593 this.mainFoot.show();
9597 Roo.each(this.el.select('tbody td', true).elements, function(e){
9598 e.on('mouseover', _this.onMouseover, _this);
9601 Roo.each(this.el.select('tbody td', true).elements, function(e){
9602 e.on('mouseout', _this.onMouseout, _this);
9604 this.fireEvent('rowsrendered', this);
9608 this.initCSS(); /// resize cols
9614 onUpdate : function(ds,record)
9616 this.refreshRow(record);
9620 onRemove : function(ds, record, index, isUpdate){
9621 if(isUpdate !== true){
9622 this.fireEvent("beforerowremoved", this, index, record);
9624 var bt = this.bodyEl.dom;
9626 var rows = this.el.select('tbody > tr', true).elements;
9628 if(typeof(rows[index]) != 'undefined'){
9629 bt.removeChild(rows[index].dom);
9632 // if(bt.rows[index]){
9633 // bt.removeChild(bt.rows[index]);
9636 if(isUpdate !== true){
9637 //this.stripeRows(index);
9638 //this.syncRowHeights(index, index);
9640 this.fireEvent("rowremoved", this, index, record);
9644 onAdd : function(ds, records, rowIndex)
9646 //Roo.log('on Add called');
9647 // - note this does not handle multiple adding very well..
9648 var bt = this.bodyEl.dom;
9649 for (var i =0 ; i < records.length;i++) {
9650 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9651 //Roo.log(records[i]);
9652 //Roo.log(this.store.getAt(rowIndex+i));
9653 this.insertRow(this.store, rowIndex + i, false);
9660 refreshRow : function(record){
9661 var ds = this.store, index;
9662 if(typeof record == 'number'){
9664 record = ds.getAt(index);
9666 index = ds.indexOf(record);
9668 return; // should not happen - but seems to
9671 this.insertRow(ds, index, true);
9673 this.onRemove(ds, record, index+1, true);
9675 //this.syncRowHeights(index, index);
9677 this.fireEvent("rowupdated", this, index, record);
9679 // private - called by RowSelection
9680 onRowSelect : function(rowIndex){
9681 var row = this.getRowDom(rowIndex);
9682 row.addClass(['bg-info','info']);
9684 // private - called by RowSelection
9685 onRowDeselect : function(rowIndex)
9690 var row = this.getRowDom(rowIndex);
9691 row.removeClass(['bg-info','info']);
9694 * Focuses the specified row.
9695 * @param {Number} row The row index
9697 focusRow : function(row)
9699 //Roo.log('GridView.focusRow');
9700 var x = this.bodyEl.dom.scrollLeft;
9701 this.focusCell(row, 0, false);
9702 this.bodyEl.dom.scrollLeft = x;
9706 * Focuses the specified cell.
9707 * @param {Number} row The row index
9708 * @param {Number} col The column index
9709 * @param {Boolean} hscroll false to disable horizontal scrolling
9711 focusCell : function(row, col, hscroll)
9713 //Roo.log('GridView.focusCell');
9714 var el = this.ensureVisible(row, col, hscroll);
9715 // not sure what focusEL achives = it's a <a> pos relative
9716 //this.focusEl.alignTo(el, "tl-tl");
9718 // this.focusEl.focus();
9720 // this.focusEl.focus.defer(1, this.focusEl);
9725 * Scrolls the specified cell into view
9726 * @param {Number} row The row index
9727 * @param {Number} col The column index
9728 * @param {Boolean} hscroll false to disable horizontal scrolling
9730 ensureVisible : function(row, col, hscroll)
9732 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9733 //return null; //disable for testing.
9734 if(typeof row != "number"){
9737 if(row < 0 && row >= this.ds.getCount()){
9740 col = (col !== undefined ? col : 0);
9742 while(cm.isHidden(col)){
9746 var el = this.getCellDom(row, col);
9750 var c = this.bodyEl.dom;
9752 var ctop = parseInt(el.offsetTop, 10);
9753 var cleft = parseInt(el.offsetLeft, 10);
9754 var cbot = ctop + el.offsetHeight;
9755 var cright = cleft + el.offsetWidth;
9757 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9758 var ch = 0; //?? header is not withing the area?
9759 var stop = parseInt(c.scrollTop, 10);
9760 var sleft = parseInt(c.scrollLeft, 10);
9761 var sbot = stop + ch;
9762 var sright = sleft + c.clientWidth;
9764 Roo.log('GridView.ensureVisible:' +
9766 ' c.clientHeight:' + c.clientHeight +
9767 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9776 //Roo.log("set scrolltop to ctop DISABLE?");
9777 }else if(cbot > sbot){
9778 //Roo.log("set scrolltop to cbot-ch");
9779 c.scrollTop = cbot-ch;
9782 if(hscroll !== false){
9784 c.scrollLeft = cleft;
9785 }else if(cright > sright){
9786 c.scrollLeft = cright-c.clientWidth;
9794 insertRow : function(dm, rowIndex, isUpdate){
9797 this.fireEvent("beforerowsinserted", this, rowIndex);
9799 //var s = this.getScrollState();
9800 var row = this.renderRow(this.cm, this.store, rowIndex);
9801 // insert before rowIndex..
9802 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9806 if(row.cellObjects.length){
9807 Roo.each(row.cellObjects, function(r){
9808 _this.renderCellObject(r);
9813 this.fireEvent("rowsinserted", this, rowIndex);
9814 //this.syncRowHeights(firstRow, lastRow);
9815 //this.stripeRows(firstRow);
9822 getRowDom : function(rowIndex)
9824 var rows = this.el.select('tbody > tr', true).elements;
9826 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9829 getCellDom : function(rowIndex, colIndex)
9831 var row = this.getRowDom(rowIndex);
9832 if (row === false) {
9835 var cols = row.select('td', true).elements;
9836 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9840 // returns the object tree for a tr..
9843 renderRow : function(cm, ds, rowIndex)
9845 var d = ds.getAt(rowIndex);
9849 cls : 'x-row-' + rowIndex,
9853 var cellObjects = [];
9855 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9856 var config = cm.config[i];
9858 var renderer = cm.getRenderer(i);
9862 if(typeof(renderer) !== 'undefined'){
9863 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9865 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9866 // and are rendered into the cells after the row is rendered - using the id for the element.
9868 if(typeof(value) === 'object'){
9878 rowIndex : rowIndex,
9883 this.fireEvent('rowclass', this, rowcfg);
9887 // this might end up displaying HTML?
9888 // this is too messy... - better to only do it on columsn you know are going to be too long
9889 //tooltip : (typeof(value) === 'object') ? '' : value,
9890 cls : rowcfg.rowClass + ' x-col-' + i,
9892 html: (typeof(value) === 'object') ? '' : value
9899 if(typeof(config.colspan) != 'undefined'){
9900 td.colspan = config.colspan;
9905 if(typeof(config.align) != 'undefined' && config.align.length){
9906 td.style += ' text-align:' + config.align + ';';
9908 if(typeof(config.valign) != 'undefined' && config.valign.length){
9909 td.style += ' vertical-align:' + config.valign + ';';
9912 if(typeof(config.width) != 'undefined'){
9913 td.style += ' width:' + config.width + 'px;';
9917 if(typeof(config.cursor) != 'undefined'){
9918 td.style += ' cursor:' + config.cursor + ';';
9921 if(typeof(config.cls) != 'undefined'){
9922 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9924 if (this.responsive) {
9925 ['xs','sm','md','lg'].map(function(size){
9927 if(typeof(config[size]) == 'undefined'){
9933 if (!config[size]) { // 0 = hidden
9934 // BS 4 '0' is treated as hide that column and below.
9935 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9939 td.cls += ' col-' + size + '-' + config[size] + (
9940 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9950 row.cellObjects = cellObjects;
9958 onBeforeLoad : function()
9967 this.el.select('tbody', true).first().dom.innerHTML = '';
9970 * Show or hide a row.
9971 * @param {Number} rowIndex to show or hide
9972 * @param {Boolean} state hide
9974 setRowVisibility : function(rowIndex, state)
9976 var bt = this.bodyEl.dom;
9978 var rows = this.el.select('tbody > tr', true).elements;
9980 if(typeof(rows[rowIndex]) == 'undefined'){
9983 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9988 getSelectionModel : function(){
9990 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9992 return this.selModel;
9995 * Render the Roo.bootstrap object from renderder
9997 renderCellObject : function(r)
10001 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10003 var t = r.cfg.render(r.container);
10006 Roo.each(r.cfg.cn, function(c){
10008 container: t.getChildContainer(),
10011 _this.renderCellObject(child);
10016 * get the Row Index from a dom element.
10017 * @param {Roo.Element} row The row to look for
10018 * @returns {Number} the row
10020 getRowIndex : function(row)
10024 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10035 * get the header TH element for columnIndex
10036 * @param {Number} columnIndex
10037 * @returns {Roo.Element}
10039 getHeaderIndex: function(colIndex)
10041 var cols = this.headEl.select('th', true).elements;
10042 return cols[colIndex];
10045 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10046 * @param {domElement} cell to look for
10047 * @returns {Number} the column
10049 getCellIndex : function(cell)
10051 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10053 return parseInt(id[1], 10);
10058 * Returns the grid's underlying element = used by panel.Grid
10059 * @return {Element} The element
10061 getGridEl : function(){
10065 * Forces a resize - used by panel.Grid
10066 * @return {Element} The element
10068 autoSize : function()
10070 //var ctr = Roo.get(this.container.dom.parentElement);
10071 var ctr = Roo.get(this.el.dom);
10073 var thd = this.getGridEl().select('thead',true).first();
10074 var tbd = this.getGridEl().select('tbody', true).first();
10075 var tfd = this.getGridEl().select('tfoot', true).first();
10077 var cw = ctr.getWidth();
10078 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10082 tbd.setWidth(ctr.getWidth());
10083 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10084 // this needs fixing for various usage - currently only hydra job advers I think..
10086 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10088 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10091 cw = Math.max(cw, this.totalWidth);
10092 this.getGridEl().select('tbody tr',true).setWidth(cw);
10095 // resize 'expandable coloumn?
10097 return; // we doe not have a view in this design..
10100 onBodyScroll: function()
10102 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10104 this.headEl.setStyle({
10105 'position' : 'relative',
10106 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10112 var scrollHeight = this.bodyEl.dom.scrollHeight;
10114 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10116 var height = this.bodyEl.getHeight();
10118 if(scrollHeight - height == scrollTop) {
10120 var total = this.ds.getTotalCount();
10122 if(this.footer.cursor + this.footer.pageSize < total){
10124 this.footer.ds.load({
10126 start : this.footer.cursor + this.footer.pageSize,
10127 limit : this.footer.pageSize
10136 onColumnSplitterMoved : function(i, diff)
10138 this.userResized = true;
10140 var cm = this.colModel;
10142 var w = this.getHeaderIndex(i).getWidth() + diff;
10145 cm.setColumnWidth(i, w, true);
10147 //var cid = cm.getColumnId(i); << not used in this version?
10148 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10150 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10151 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10152 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10154 //this.updateSplitters();
10155 //this.layout(); << ??
10156 this.fireEvent("columnresize", i, w);
10158 onHeaderChange : function()
10160 var header = this.renderHeader();
10161 var table = this.el.select('table', true).first();
10163 this.headEl.remove();
10164 this.headEl = table.createChild(header, this.bodyEl, false);
10166 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10167 e.on('click', this.sort, this);
10170 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10171 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10176 onHiddenChange : function(colModel, colIndex, hidden)
10179 this.cm.setHidden()
10180 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10181 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10183 this.CSS.updateRule(thSelector, "display", "");
10184 this.CSS.updateRule(tdSelector, "display", "");
10187 this.CSS.updateRule(thSelector, "display", "none");
10188 this.CSS.updateRule(tdSelector, "display", "none");
10191 // onload calls initCSS()
10192 this.onHeaderChange();
10196 setColumnWidth: function(col_index, width)
10198 // width = "md-2 xs-2..."
10199 if(!this.colModel.config[col_index]) {
10203 var w = width.split(" ");
10205 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10207 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10210 for(var j = 0; j < w.length; j++) {
10216 var size_cls = w[j].split("-");
10218 if(!Number.isInteger(size_cls[1] * 1)) {
10222 if(!this.colModel.config[col_index][size_cls[0]]) {
10226 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10230 h_row[0].classList.replace(
10231 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10232 "col-"+size_cls[0]+"-"+size_cls[1]
10235 for(var i = 0; i < rows.length; i++) {
10237 var size_cls = w[j].split("-");
10239 if(!Number.isInteger(size_cls[1] * 1)) {
10243 if(!this.colModel.config[col_index][size_cls[0]]) {
10247 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10251 rows[i].classList.replace(
10252 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10253 "col-"+size_cls[0]+"-"+size_cls[1]
10257 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10262 // currently only used to find the split on drag..
10263 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10268 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10269 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10278 * @class Roo.bootstrap.TableCell
10279 * @extends Roo.bootstrap.Component
10280 * Bootstrap TableCell class
10281 * @cfg {String} html cell contain text
10282 * @cfg {String} cls cell class
10283 * @cfg {String} tag cell tag (td|th) default td
10284 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10285 * @cfg {String} align Aligns the content in a cell
10286 * @cfg {String} axis Categorizes cells
10287 * @cfg {String} bgcolor Specifies the background color of a cell
10288 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10289 * @cfg {Number} colspan Specifies the number of columns a cell should span
10290 * @cfg {String} headers Specifies one or more header cells a cell is related to
10291 * @cfg {Number} height Sets the height of a cell
10292 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10293 * @cfg {Number} rowspan Sets the number of rows a cell should span
10294 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10295 * @cfg {String} valign Vertical aligns the content in a cell
10296 * @cfg {Number} width Specifies the width of a cell
10299 * Create a new TableCell
10300 * @param {Object} config The config object
10303 Roo.bootstrap.TableCell = function(config){
10304 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10307 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10327 getAutoCreate : function(){
10328 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10335 cfg.tag = this.tag;
10348 cfg.align=this.align
10353 if (this.bgcolor) {
10354 cfg.bgcolor=this.bgcolor
10356 if (this.charoff) {
10357 cfg.charoff=this.charoff
10359 if (this.colspan) {
10360 cfg.colspan=this.colspan
10362 if (this.headers) {
10363 cfg.headers=this.headers
10366 cfg.height=this.height
10369 cfg.nowrap=this.nowrap
10371 if (this.rowspan) {
10372 cfg.rowspan=this.rowspan
10375 cfg.scope=this.scope
10378 cfg.valign=this.valign
10381 cfg.width=this.width
10400 * @class Roo.bootstrap.TableRow
10401 * @extends Roo.bootstrap.Component
10402 * Bootstrap TableRow class
10403 * @cfg {String} cls row class
10404 * @cfg {String} align Aligns the content in a table row
10405 * @cfg {String} bgcolor Specifies a background color for a table row
10406 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10407 * @cfg {String} valign Vertical aligns the content in a table row
10410 * Create a new TableRow
10411 * @param {Object} config The config object
10414 Roo.bootstrap.TableRow = function(config){
10415 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10418 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10426 getAutoCreate : function(){
10427 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10434 cfg.cls = this.cls;
10437 cfg.align = this.align;
10440 cfg.bgcolor = this.bgcolor;
10443 cfg.charoff = this.charoff;
10446 cfg.valign = this.valign;
10464 * @class Roo.bootstrap.TableBody
10465 * @extends Roo.bootstrap.Component
10466 * Bootstrap TableBody class
10467 * @cfg {String} cls element class
10468 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10469 * @cfg {String} align Aligns the content inside the element
10470 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10471 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10474 * Create a new TableBody
10475 * @param {Object} config The config object
10478 Roo.bootstrap.TableBody = function(config){
10479 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10482 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10490 getAutoCreate : function(){
10491 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10501 cfg.tag = this.tag;
10505 cfg.align = this.align;
10508 cfg.charoff = this.charoff;
10511 cfg.valign = this.valign;
10518 // initEvents : function()
10521 // if(!this.store){
10525 // this.store = Roo.factory(this.store, Roo.data);
10526 // this.store.on('load', this.onLoad, this);
10528 // this.store.load();
10532 // onLoad: function ()
10534 // this.fireEvent('load', this);
10544 * Ext JS Library 1.1.1
10545 * Copyright(c) 2006-2007, Ext JS, LLC.
10547 * Originally Released Under LGPL - original licence link has changed is not relivant.
10550 * <script type="text/javascript">
10553 // as we use this in bootstrap.
10554 Roo.namespace('Roo.form');
10556 * @class Roo.form.Action
10557 * Internal Class used to handle form actions
10559 * @param {Roo.form.BasicForm} el The form element or its id
10560 * @param {Object} config Configuration options
10565 // define the action interface
10566 Roo.form.Action = function(form, options){
10568 this.options = options || {};
10571 * Client Validation Failed
10574 Roo.form.Action.CLIENT_INVALID = 'client';
10576 * Server Validation Failed
10579 Roo.form.Action.SERVER_INVALID = 'server';
10581 * Connect to Server Failed
10584 Roo.form.Action.CONNECT_FAILURE = 'connect';
10586 * Reading Data from Server Failed
10589 Roo.form.Action.LOAD_FAILURE = 'load';
10591 Roo.form.Action.prototype = {
10593 failureType : undefined,
10594 response : undefined,
10595 result : undefined,
10597 // interface method
10598 run : function(options){
10602 // interface method
10603 success : function(response){
10607 // interface method
10608 handleResponse : function(response){
10612 // default connection failure
10613 failure : function(response){
10615 this.response = response;
10616 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10617 this.form.afterAction(this, false);
10620 processResponse : function(response){
10621 this.response = response;
10622 if(!response.responseText){
10625 this.result = this.handleResponse(response);
10626 return this.result;
10629 // utility functions used internally
10630 getUrl : function(appendParams){
10631 var url = this.options.url || this.form.url || this.form.el.dom.action;
10633 var p = this.getParams();
10635 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10641 getMethod : function(){
10642 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10645 getParams : function(){
10646 var bp = this.form.baseParams;
10647 var p = this.options.params;
10649 if(typeof p == "object"){
10650 p = Roo.urlEncode(Roo.applyIf(p, bp));
10651 }else if(typeof p == 'string' && bp){
10652 p += '&' + Roo.urlEncode(bp);
10655 p = Roo.urlEncode(bp);
10660 createCallback : function(){
10662 success: this.success,
10663 failure: this.failure,
10665 timeout: (this.form.timeout*1000),
10666 upload: this.form.fileUpload ? this.success : undefined
10671 Roo.form.Action.Submit = function(form, options){
10672 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10675 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10678 haveProgress : false,
10679 uploadComplete : false,
10681 // uploadProgress indicator.
10682 uploadProgress : function()
10684 if (!this.form.progressUrl) {
10688 if (!this.haveProgress) {
10689 Roo.MessageBox.progress("Uploading", "Uploading");
10691 if (this.uploadComplete) {
10692 Roo.MessageBox.hide();
10696 this.haveProgress = true;
10698 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10700 var c = new Roo.data.Connection();
10702 url : this.form.progressUrl,
10707 success : function(req){
10708 //console.log(data);
10712 rdata = Roo.decode(req.responseText)
10714 Roo.log("Invalid data from server..");
10718 if (!rdata || !rdata.success) {
10720 Roo.MessageBox.alert(Roo.encode(rdata));
10723 var data = rdata.data;
10725 if (this.uploadComplete) {
10726 Roo.MessageBox.hide();
10731 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10732 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10735 this.uploadProgress.defer(2000,this);
10738 failure: function(data) {
10739 Roo.log('progress url failed ');
10750 // run get Values on the form, so it syncs any secondary forms.
10751 this.form.getValues();
10753 var o = this.options;
10754 var method = this.getMethod();
10755 var isPost = method == 'POST';
10756 if(o.clientValidation === false || this.form.isValid()){
10758 if (this.form.progressUrl) {
10759 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10760 (new Date() * 1) + '' + Math.random());
10765 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10766 form:this.form.el.dom,
10767 url:this.getUrl(!isPost),
10769 params:isPost ? this.getParams() : null,
10770 isUpload: this.form.fileUpload,
10771 formData : this.form.formData
10774 this.uploadProgress();
10776 }else if (o.clientValidation !== false){ // client validation failed
10777 this.failureType = Roo.form.Action.CLIENT_INVALID;
10778 this.form.afterAction(this, false);
10782 success : function(response)
10784 this.uploadComplete= true;
10785 if (this.haveProgress) {
10786 Roo.MessageBox.hide();
10790 var result = this.processResponse(response);
10791 if(result === true || result.success){
10792 this.form.afterAction(this, true);
10796 this.form.markInvalid(result.errors);
10797 this.failureType = Roo.form.Action.SERVER_INVALID;
10799 this.form.afterAction(this, false);
10801 failure : function(response)
10803 this.uploadComplete= true;
10804 if (this.haveProgress) {
10805 Roo.MessageBox.hide();
10808 this.response = response;
10809 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10810 this.form.afterAction(this, false);
10813 handleResponse : function(response){
10814 if(this.form.errorReader){
10815 var rs = this.form.errorReader.read(response);
10818 for(var i = 0, len = rs.records.length; i < len; i++) {
10819 var r = rs.records[i];
10820 errors[i] = r.data;
10823 if(errors.length < 1){
10827 success : rs.success,
10833 ret = Roo.decode(response.responseText);
10837 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10847 Roo.form.Action.Load = function(form, options){
10848 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10849 this.reader = this.form.reader;
10852 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10857 Roo.Ajax.request(Roo.apply(
10858 this.createCallback(), {
10859 method:this.getMethod(),
10860 url:this.getUrl(false),
10861 params:this.getParams()
10865 success : function(response){
10867 var result = this.processResponse(response);
10868 if(result === true || !result.success || !result.data){
10869 this.failureType = Roo.form.Action.LOAD_FAILURE;
10870 this.form.afterAction(this, false);
10873 this.form.clearInvalid();
10874 this.form.setValues(result.data);
10875 this.form.afterAction(this, true);
10878 handleResponse : function(response){
10879 if(this.form.reader){
10880 var rs = this.form.reader.read(response);
10881 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10883 success : rs.success,
10887 return Roo.decode(response.responseText);
10891 Roo.form.Action.ACTION_TYPES = {
10892 'load' : Roo.form.Action.Load,
10893 'submit' : Roo.form.Action.Submit
10902 * @class Roo.bootstrap.Form
10903 * @extends Roo.bootstrap.Component
10904 * Bootstrap Form class
10905 * @cfg {String} method GET | POST (default POST)
10906 * @cfg {String} labelAlign top | left (default top)
10907 * @cfg {String} align left | right - for navbars
10908 * @cfg {Boolean} loadMask load mask when submit (default true)
10912 * Create a new Form
10913 * @param {Object} config The config object
10917 Roo.bootstrap.Form = function(config){
10919 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10921 Roo.bootstrap.Form.popover.apply();
10925 * @event clientvalidation
10926 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10927 * @param {Form} this
10928 * @param {Boolean} valid true if the form has passed client-side validation
10930 clientvalidation: true,
10932 * @event beforeaction
10933 * Fires before any action is performed. Return false to cancel the action.
10934 * @param {Form} this
10935 * @param {Action} action The action to be performed
10937 beforeaction: true,
10939 * @event actionfailed
10940 * Fires when an action fails.
10941 * @param {Form} this
10942 * @param {Action} action The action that failed
10944 actionfailed : true,
10946 * @event actioncomplete
10947 * Fires when an action is completed.
10948 * @param {Form} this
10949 * @param {Action} action The action that completed
10951 actioncomplete : true
10955 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10958 * @cfg {String} method
10959 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10963 * @cfg {String} url
10964 * The URL to use for form actions if one isn't supplied in the action options.
10967 * @cfg {Boolean} fileUpload
10968 * Set to true if this form is a file upload.
10972 * @cfg {Object} baseParams
10973 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10977 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10981 * @cfg {Sting} align (left|right) for navbar forms
10986 activeAction : null,
10989 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10990 * element by passing it or its id or mask the form itself by passing in true.
10993 waitMsgTarget : false,
10998 * @cfg {Boolean} errorMask (true|false) default false
11003 * @cfg {Number} maskOffset Default 100
11008 * @cfg {Boolean} maskBody
11012 getAutoCreate : function(){
11016 method : this.method || 'POST',
11017 id : this.id || Roo.id(),
11020 if (this.parent().xtype.match(/^Nav/)) {
11021 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11025 if (this.labelAlign == 'left' ) {
11026 cfg.cls += ' form-horizontal';
11032 initEvents : function()
11034 this.el.on('submit', this.onSubmit, this);
11035 // this was added as random key presses on the form where triggering form submit.
11036 this.el.on('keypress', function(e) {
11037 if (e.getCharCode() != 13) {
11040 // we might need to allow it for textareas.. and some other items.
11041 // check e.getTarget().
11043 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11047 Roo.log("keypress blocked");
11049 e.preventDefault();
11055 onSubmit : function(e){
11060 * Returns true if client-side validation on the form is successful.
11063 isValid : function(){
11064 var items = this.getItems();
11066 var target = false;
11068 items.each(function(f){
11074 Roo.log('invalid field: ' + f.name);
11078 if(!target && f.el.isVisible(true)){
11084 if(this.errorMask && !valid){
11085 Roo.bootstrap.Form.popover.mask(this, target);
11092 * Returns true if any fields in this form have changed since their original load.
11095 isDirty : function(){
11097 var items = this.getItems();
11098 items.each(function(f){
11108 * Performs a predefined action (submit or load) or custom actions you define on this form.
11109 * @param {String} actionName The name of the action type
11110 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11111 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11112 * accept other config options):
11114 Property Type Description
11115 ---------------- --------------- ----------------------------------------------------------------------------------
11116 url String The url for the action (defaults to the form's url)
11117 method String The form method to use (defaults to the form's method, or POST if not defined)
11118 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11119 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11120 validate the form on the client (defaults to false)
11122 * @return {BasicForm} this
11124 doAction : function(action, options){
11125 if(typeof action == 'string'){
11126 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11128 if(this.fireEvent('beforeaction', this, action) !== false){
11129 this.beforeAction(action);
11130 action.run.defer(100, action);
11136 beforeAction : function(action){
11137 var o = action.options;
11142 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11144 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11147 // not really supported yet.. ??
11149 //if(this.waitMsgTarget === true){
11150 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11151 //}else if(this.waitMsgTarget){
11152 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11153 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11155 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11161 afterAction : function(action, success){
11162 this.activeAction = null;
11163 var o = action.options;
11168 Roo.get(document.body).unmask();
11174 //if(this.waitMsgTarget === true){
11175 // this.el.unmask();
11176 //}else if(this.waitMsgTarget){
11177 // this.waitMsgTarget.unmask();
11179 // Roo.MessageBox.updateProgress(1);
11180 // Roo.MessageBox.hide();
11187 Roo.callback(o.success, o.scope, [this, action]);
11188 this.fireEvent('actioncomplete', this, action);
11192 // failure condition..
11193 // we have a scenario where updates need confirming.
11194 // eg. if a locking scenario exists..
11195 // we look for { errors : { needs_confirm : true }} in the response.
11197 (typeof(action.result) != 'undefined') &&
11198 (typeof(action.result.errors) != 'undefined') &&
11199 (typeof(action.result.errors.needs_confirm) != 'undefined')
11202 Roo.log("not supported yet");
11205 Roo.MessageBox.confirm(
11206 "Change requires confirmation",
11207 action.result.errorMsg,
11212 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11222 Roo.callback(o.failure, o.scope, [this, action]);
11223 // show an error message if no failed handler is set..
11224 if (!this.hasListener('actionfailed')) {
11225 Roo.log("need to add dialog support");
11227 Roo.MessageBox.alert("Error",
11228 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11229 action.result.errorMsg :
11230 "Saving Failed, please check your entries or try again"
11235 this.fireEvent('actionfailed', this, action);
11240 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11241 * @param {String} id The value to search for
11244 findField : function(id){
11245 var items = this.getItems();
11246 var field = items.get(id);
11248 items.each(function(f){
11249 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11256 return field || null;
11259 * Mark fields in this form invalid in bulk.
11260 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11261 * @return {BasicForm} this
11263 markInvalid : function(errors){
11264 if(errors instanceof Array){
11265 for(var i = 0, len = errors.length; i < len; i++){
11266 var fieldError = errors[i];
11267 var f = this.findField(fieldError.id);
11269 f.markInvalid(fieldError.msg);
11275 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11276 field.markInvalid(errors[id]);
11280 //Roo.each(this.childForms || [], function (f) {
11281 // f.markInvalid(errors);
11288 * Set values for fields in this form in bulk.
11289 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11290 * @return {BasicForm} this
11292 setValues : function(values){
11293 if(values instanceof Array){ // array of objects
11294 for(var i = 0, len = values.length; i < len; i++){
11296 var f = this.findField(v.id);
11298 f.setValue(v.value);
11299 if(this.trackResetOnLoad){
11300 f.originalValue = f.getValue();
11304 }else{ // object hash
11307 if(typeof values[id] != 'function' && (field = this.findField(id))){
11309 if (field.setFromData &&
11310 field.valueField &&
11311 field.displayField &&
11312 // combos' with local stores can
11313 // be queried via setValue()
11314 // to set their value..
11315 (field.store && !field.store.isLocal)
11319 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11320 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11321 field.setFromData(sd);
11323 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11325 field.setFromData(values);
11328 field.setValue(values[id]);
11332 if(this.trackResetOnLoad){
11333 field.originalValue = field.getValue();
11339 //Roo.each(this.childForms || [], function (f) {
11340 // f.setValues(values);
11347 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11348 * they are returned as an array.
11349 * @param {Boolean} asString
11352 getValues : function(asString){
11353 //if (this.childForms) {
11354 // copy values from the child forms
11355 // Roo.each(this.childForms, function (f) {
11356 // this.setValues(f.getValues());
11362 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11363 if(asString === true){
11366 return Roo.urlDecode(fs);
11370 * Returns the fields in this form as an object with key/value pairs.
11371 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11374 getFieldValues : function(with_hidden)
11376 var items = this.getItems();
11378 items.each(function(f){
11380 if (!f.getName()) {
11384 var v = f.getValue();
11386 if (f.inputType =='radio') {
11387 if (typeof(ret[f.getName()]) == 'undefined') {
11388 ret[f.getName()] = ''; // empty..
11391 if (!f.el.dom.checked) {
11395 v = f.el.dom.value;
11399 if(f.xtype == 'MoneyField'){
11400 ret[f.currencyName] = f.getCurrency();
11403 // not sure if this supported any more..
11404 if ((typeof(v) == 'object') && f.getRawValue) {
11405 v = f.getRawValue() ; // dates..
11407 // combo boxes where name != hiddenName...
11408 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11409 ret[f.name] = f.getRawValue();
11411 ret[f.getName()] = v;
11418 * Clears all invalid messages in this form.
11419 * @return {BasicForm} this
11421 clearInvalid : function(){
11422 var items = this.getItems();
11424 items.each(function(f){
11432 * Resets this form.
11433 * @return {BasicForm} this
11435 reset : function(){
11436 var items = this.getItems();
11437 items.each(function(f){
11441 Roo.each(this.childForms || [], function (f) {
11449 getItems : function()
11451 var r=new Roo.util.MixedCollection(false, function(o){
11452 return o.id || (o.id = Roo.id());
11454 var iter = function(el) {
11461 Roo.each(el.items,function(e) {
11470 hideFields : function(items)
11472 Roo.each(items, function(i){
11474 var f = this.findField(i);
11485 showFields : function(items)
11487 Roo.each(items, function(i){
11489 var f = this.findField(i);
11502 Roo.apply(Roo.bootstrap.Form, {
11518 intervalID : false,
11524 if(this.isApplied){
11529 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11530 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11531 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11532 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11535 this.maskEl.top.enableDisplayMode("block");
11536 this.maskEl.left.enableDisplayMode("block");
11537 this.maskEl.bottom.enableDisplayMode("block");
11538 this.maskEl.right.enableDisplayMode("block");
11540 this.toolTip = new Roo.bootstrap.Tooltip({
11541 cls : 'roo-form-error-popover',
11543 'left' : ['r-l', [-2,0], 'right'],
11544 'right' : ['l-r', [2,0], 'left'],
11545 'bottom' : ['tl-bl', [0,2], 'top'],
11546 'top' : [ 'bl-tl', [0,-2], 'bottom']
11550 this.toolTip.render(Roo.get(document.body));
11552 this.toolTip.el.enableDisplayMode("block");
11554 Roo.get(document.body).on('click', function(){
11558 Roo.get(document.body).on('touchstart', function(){
11562 this.isApplied = true
11565 mask : function(form, target)
11569 this.target = target;
11571 if(!this.form.errorMask || !target.el){
11575 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11577 Roo.log(scrollable);
11579 var ot = this.target.el.calcOffsetsTo(scrollable);
11581 var scrollTo = ot[1] - this.form.maskOffset;
11583 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11585 scrollable.scrollTo('top', scrollTo);
11587 var box = this.target.el.getBox();
11589 var zIndex = Roo.bootstrap.Modal.zIndex++;
11592 this.maskEl.top.setStyle('position', 'absolute');
11593 this.maskEl.top.setStyle('z-index', zIndex);
11594 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11595 this.maskEl.top.setLeft(0);
11596 this.maskEl.top.setTop(0);
11597 this.maskEl.top.show();
11599 this.maskEl.left.setStyle('position', 'absolute');
11600 this.maskEl.left.setStyle('z-index', zIndex);
11601 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11602 this.maskEl.left.setLeft(0);
11603 this.maskEl.left.setTop(box.y - this.padding);
11604 this.maskEl.left.show();
11606 this.maskEl.bottom.setStyle('position', 'absolute');
11607 this.maskEl.bottom.setStyle('z-index', zIndex);
11608 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11609 this.maskEl.bottom.setLeft(0);
11610 this.maskEl.bottom.setTop(box.bottom + this.padding);
11611 this.maskEl.bottom.show();
11613 this.maskEl.right.setStyle('position', 'absolute');
11614 this.maskEl.right.setStyle('z-index', zIndex);
11615 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11616 this.maskEl.right.setLeft(box.right + this.padding);
11617 this.maskEl.right.setTop(box.y - this.padding);
11618 this.maskEl.right.show();
11620 this.toolTip.bindEl = this.target.el;
11622 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11624 var tip = this.target.blankText;
11626 if(this.target.getValue() !== '' ) {
11628 if (this.target.invalidText.length) {
11629 tip = this.target.invalidText;
11630 } else if (this.target.regexText.length){
11631 tip = this.target.regexText;
11635 this.toolTip.show(tip);
11637 this.intervalID = window.setInterval(function() {
11638 Roo.bootstrap.Form.popover.unmask();
11641 window.onwheel = function(){ return false;};
11643 (function(){ this.isMasked = true; }).defer(500, this);
11647 unmask : function()
11649 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11653 this.maskEl.top.setStyle('position', 'absolute');
11654 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11655 this.maskEl.top.hide();
11657 this.maskEl.left.setStyle('position', 'absolute');
11658 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11659 this.maskEl.left.hide();
11661 this.maskEl.bottom.setStyle('position', 'absolute');
11662 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11663 this.maskEl.bottom.hide();
11665 this.maskEl.right.setStyle('position', 'absolute');
11666 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11667 this.maskEl.right.hide();
11669 this.toolTip.hide();
11671 this.toolTip.el.hide();
11673 window.onwheel = function(){ return true;};
11675 if(this.intervalID){
11676 window.clearInterval(this.intervalID);
11677 this.intervalID = false;
11680 this.isMasked = false;
11690 * Ext JS Library 1.1.1
11691 * Copyright(c) 2006-2007, Ext JS, LLC.
11693 * Originally Released Under LGPL - original licence link has changed is not relivant.
11696 * <script type="text/javascript">
11699 * @class Roo.form.VTypes
11700 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11703 Roo.form.VTypes = function(){
11704 // closure these in so they are only created once.
11705 var alpha = /^[a-zA-Z_]+$/;
11706 var alphanum = /^[a-zA-Z0-9_]+$/;
11707 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11708 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11710 // All these messages and functions are configurable
11713 * The function used to validate email addresses
11714 * @param {String} value The email address
11716 'email' : function(v){
11717 return email.test(v);
11720 * The error text to display when the email validation function returns false
11723 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11725 * The keystroke filter mask to be applied on email input
11728 'emailMask' : /[a-z0-9_\.\-@]/i,
11731 * The function used to validate URLs
11732 * @param {String} value The URL
11734 'url' : function(v){
11735 return url.test(v);
11738 * The error text to display when the url validation function returns false
11741 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11744 * The function used to validate alpha values
11745 * @param {String} value The value
11747 'alpha' : function(v){
11748 return alpha.test(v);
11751 * The error text to display when the alpha validation function returns false
11754 'alphaText' : 'This field should only contain letters and _',
11756 * The keystroke filter mask to be applied on alpha input
11759 'alphaMask' : /[a-z_]/i,
11762 * The function used to validate alphanumeric values
11763 * @param {String} value The value
11765 'alphanum' : function(v){
11766 return alphanum.test(v);
11769 * The error text to display when the alphanumeric validation function returns false
11772 'alphanumText' : 'This field should only contain letters, numbers and _',
11774 * The keystroke filter mask to be applied on alphanumeric input
11777 'alphanumMask' : /[a-z0-9_]/i
11787 * @class Roo.bootstrap.Input
11788 * @extends Roo.bootstrap.Component
11789 * Bootstrap Input class
11790 * @cfg {Boolean} disabled is it disabled
11791 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11792 * @cfg {String} name name of the input
11793 * @cfg {string} fieldLabel - the label associated
11794 * @cfg {string} placeholder - placeholder to put in text.
11795 * @cfg {string} before - input group add on before
11796 * @cfg {string} after - input group add on after
11797 * @cfg {string} size - (lg|sm) or leave empty..
11798 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11799 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11800 * @cfg {Number} md colspan out of 12 for computer-sized screens
11801 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11802 * @cfg {string} value default value of the input
11803 * @cfg {Number} labelWidth set the width of label
11804 * @cfg {Number} labellg set the width of label (1-12)
11805 * @cfg {Number} labelmd set the width of label (1-12)
11806 * @cfg {Number} labelsm set the width of label (1-12)
11807 * @cfg {Number} labelxs set the width of label (1-12)
11808 * @cfg {String} labelAlign (top|left)
11809 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11810 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11811 * @cfg {String} indicatorpos (left|right) default left
11812 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11813 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11814 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11816 * @cfg {String} align (left|center|right) Default left
11817 * @cfg {Boolean} forceFeedback (true|false) Default false
11820 * Create a new Input
11821 * @param {Object} config The config object
11824 Roo.bootstrap.Input = function(config){
11826 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11831 * Fires when this field receives input focus.
11832 * @param {Roo.form.Field} this
11837 * Fires when this field loses input focus.
11838 * @param {Roo.form.Field} this
11842 * @event specialkey
11843 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11844 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11845 * @param {Roo.form.Field} this
11846 * @param {Roo.EventObject} e The event object
11851 * Fires just before the field blurs if the field value has changed.
11852 * @param {Roo.form.Field} this
11853 * @param {Mixed} newValue The new value
11854 * @param {Mixed} oldValue The original value
11859 * Fires after the field has been marked as invalid.
11860 * @param {Roo.form.Field} this
11861 * @param {String} msg The validation message
11866 * Fires after the field has been validated with no errors.
11867 * @param {Roo.form.Field} this
11872 * Fires after the key up
11873 * @param {Roo.form.Field} this
11874 * @param {Roo.EventObject} e The event Object
11879 * Fires after the user pastes into input
11880 * @param {Roo.form.Field} this
11881 * @param {Roo.EventObject} e The event Object
11887 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11889 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11890 automatic validation (defaults to "keyup").
11892 validationEvent : "keyup",
11894 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11896 validateOnBlur : true,
11898 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11900 validationDelay : 250,
11902 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11904 focusClass : "x-form-focus", // not needed???
11908 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11910 invalidClass : "has-warning",
11913 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11915 validClass : "has-success",
11918 * @cfg {Boolean} hasFeedback (true|false) default true
11920 hasFeedback : true,
11923 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11925 invalidFeedbackClass : "glyphicon-warning-sign",
11928 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11930 validFeedbackClass : "glyphicon-ok",
11933 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11935 selectOnFocus : false,
11938 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11942 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11947 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11949 disableKeyFilter : false,
11952 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11956 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11960 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11962 blankText : "Please complete this mandatory field",
11965 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11969 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11971 maxLength : Number.MAX_VALUE,
11973 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11975 minLengthText : "The minimum length for this field is {0}",
11977 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11979 maxLengthText : "The maximum length for this field is {0}",
11983 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11984 * If available, this function will be called only after the basic validators all return true, and will be passed the
11985 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11989 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11990 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11991 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11995 * @cfg {String} regexText -- Depricated - use Invalid Text
12000 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12006 autocomplete: false,
12010 inputType : 'text',
12013 placeholder: false,
12018 preventMark: false,
12019 isFormField : true,
12022 labelAlign : false,
12025 formatedValue : false,
12026 forceFeedback : false,
12028 indicatorpos : 'left',
12038 parentLabelAlign : function()
12041 while (parent.parent()) {
12042 parent = parent.parent();
12043 if (typeof(parent.labelAlign) !='undefined') {
12044 return parent.labelAlign;
12051 getAutoCreate : function()
12053 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12059 if(this.inputType != 'hidden'){
12060 cfg.cls = 'form-group' //input-group
12066 type : this.inputType,
12067 value : this.value,
12068 cls : 'form-control',
12069 placeholder : this.placeholder || '',
12070 autocomplete : this.autocomplete || 'new-password'
12072 if (this.inputType == 'file') {
12073 input.style = 'overflow:hidden'; // why not in CSS?
12076 if(this.capture.length){
12077 input.capture = this.capture;
12080 if(this.accept.length){
12081 input.accept = this.accept + "/*";
12085 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12088 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12089 input.maxLength = this.maxLength;
12092 if (this.disabled) {
12093 input.disabled=true;
12096 if (this.readOnly) {
12097 input.readonly=true;
12101 input.name = this.name;
12105 input.cls += ' input-' + this.size;
12109 ['xs','sm','md','lg'].map(function(size){
12110 if (settings[size]) {
12111 cfg.cls += ' col-' + size + '-' + settings[size];
12115 var inputblock = input;
12119 cls: 'glyphicon form-control-feedback'
12122 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12125 cls : 'has-feedback',
12133 if (this.before || this.after) {
12136 cls : 'input-group',
12140 if (this.before && typeof(this.before) == 'string') {
12142 inputblock.cn.push({
12144 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12148 if (this.before && typeof(this.before) == 'object') {
12149 this.before = Roo.factory(this.before);
12151 inputblock.cn.push({
12153 cls : 'roo-input-before input-group-prepend input-group-' +
12154 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12158 inputblock.cn.push(input);
12160 if (this.after && typeof(this.after) == 'string') {
12161 inputblock.cn.push({
12163 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12167 if (this.after && typeof(this.after) == 'object') {
12168 this.after = Roo.factory(this.after);
12170 inputblock.cn.push({
12172 cls : 'roo-input-after input-group-append input-group-' +
12173 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12177 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12178 inputblock.cls += ' has-feedback';
12179 inputblock.cn.push(feedback);
12184 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12185 tooltip : 'This field is required'
12187 if (this.allowBlank ) {
12188 indicator.style = this.allowBlank ? ' display:none' : '';
12190 if (align ==='left' && this.fieldLabel.length) {
12192 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12199 cls : 'control-label col-form-label',
12200 html : this.fieldLabel
12211 var labelCfg = cfg.cn[1];
12212 var contentCfg = cfg.cn[2];
12214 if(this.indicatorpos == 'right'){
12219 cls : 'control-label col-form-label',
12223 html : this.fieldLabel
12237 labelCfg = cfg.cn[0];
12238 contentCfg = cfg.cn[1];
12242 if(this.labelWidth > 12){
12243 labelCfg.style = "width: " + this.labelWidth + 'px';
12246 if(this.labelWidth < 13 && this.labelmd == 0){
12247 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12250 if(this.labellg > 0){
12251 labelCfg.cls += ' col-lg-' + this.labellg;
12252 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12255 if(this.labelmd > 0){
12256 labelCfg.cls += ' col-md-' + this.labelmd;
12257 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12260 if(this.labelsm > 0){
12261 labelCfg.cls += ' col-sm-' + this.labelsm;
12262 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12265 if(this.labelxs > 0){
12266 labelCfg.cls += ' col-xs-' + this.labelxs;
12267 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12271 } else if ( this.fieldLabel.length) {
12278 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12279 tooltip : 'This field is required',
12280 style : this.allowBlank ? ' display:none' : ''
12284 //cls : 'input-group-addon',
12285 html : this.fieldLabel
12293 if(this.indicatorpos == 'right'){
12298 //cls : 'input-group-addon',
12299 html : this.fieldLabel
12304 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12305 tooltip : 'This field is required',
12306 style : this.allowBlank ? ' display:none' : ''
12326 if (this.parentType === 'Navbar' && this.parent().bar) {
12327 cfg.cls += ' navbar-form';
12330 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12331 // on BS4 we do this only if not form
12332 cfg.cls += ' navbar-form';
12340 * return the real input element.
12342 inputEl: function ()
12344 return this.el.select('input.form-control',true).first();
12347 tooltipEl : function()
12349 return this.inputEl();
12352 indicatorEl : function()
12354 if (Roo.bootstrap.version == 4) {
12355 return false; // not enabled in v4 yet.
12358 var indicator = this.el.select('i.roo-required-indicator',true).first();
12368 setDisabled : function(v)
12370 var i = this.inputEl().dom;
12372 i.removeAttribute('disabled');
12376 i.setAttribute('disabled','true');
12378 initEvents : function()
12381 this.inputEl().on("keydown" , this.fireKey, this);
12382 this.inputEl().on("focus", this.onFocus, this);
12383 this.inputEl().on("blur", this.onBlur, this);
12385 this.inputEl().relayEvent('keyup', this);
12386 this.inputEl().relayEvent('paste', this);
12388 this.indicator = this.indicatorEl();
12390 if(this.indicator){
12391 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12394 // reference to original value for reset
12395 this.originalValue = this.getValue();
12396 //Roo.form.TextField.superclass.initEvents.call(this);
12397 if(this.validationEvent == 'keyup'){
12398 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12399 this.inputEl().on('keyup', this.filterValidation, this);
12401 else if(this.validationEvent !== false){
12402 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12405 if(this.selectOnFocus){
12406 this.on("focus", this.preFocus, this);
12409 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12410 this.inputEl().on("keypress", this.filterKeys, this);
12412 this.inputEl().relayEvent('keypress', this);
12415 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12416 this.el.on("click", this.autoSize, this);
12419 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12420 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12423 if (typeof(this.before) == 'object') {
12424 this.before.render(this.el.select('.roo-input-before',true).first());
12426 if (typeof(this.after) == 'object') {
12427 this.after.render(this.el.select('.roo-input-after',true).first());
12430 this.inputEl().on('change', this.onChange, this);
12433 filterValidation : function(e){
12434 if(!e.isNavKeyPress()){
12435 this.validationTask.delay(this.validationDelay);
12439 * Validates the field value
12440 * @return {Boolean} True if the value is valid, else false
12442 validate : function(){
12443 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12444 if(this.disabled || this.validateValue(this.getRawValue())){
12449 this.markInvalid();
12455 * Validates a value according to the field's validation rules and marks the field as invalid
12456 * if the validation fails
12457 * @param {Mixed} value The value to validate
12458 * @return {Boolean} True if the value is valid, else false
12460 validateValue : function(value)
12462 if(this.getVisibilityEl().hasClass('hidden')){
12466 if(value.length < 1) { // if it's blank
12467 if(this.allowBlank){
12473 if(value.length < this.minLength){
12476 if(value.length > this.maxLength){
12480 var vt = Roo.form.VTypes;
12481 if(!vt[this.vtype](value, this)){
12485 if(typeof this.validator == "function"){
12486 var msg = this.validator(value);
12490 if (typeof(msg) == 'string') {
12491 this.invalidText = msg;
12495 if(this.regex && !this.regex.test(value)){
12503 fireKey : function(e){
12504 //Roo.log('field ' + e.getKey());
12505 if(e.isNavKeyPress()){
12506 this.fireEvent("specialkey", this, e);
12509 focus : function (selectText){
12511 this.inputEl().focus();
12512 if(selectText === true){
12513 this.inputEl().dom.select();
12519 onFocus : function(){
12520 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12521 // this.el.addClass(this.focusClass);
12523 if(!this.hasFocus){
12524 this.hasFocus = true;
12525 this.startValue = this.getValue();
12526 this.fireEvent("focus", this);
12530 beforeBlur : Roo.emptyFn,
12534 onBlur : function(){
12536 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12537 //this.el.removeClass(this.focusClass);
12539 this.hasFocus = false;
12540 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12543 var v = this.getValue();
12544 if(String(v) !== String(this.startValue)){
12545 this.fireEvent('change', this, v, this.startValue);
12547 this.fireEvent("blur", this);
12550 onChange : function(e)
12552 var v = this.getValue();
12553 if(String(v) !== String(this.startValue)){
12554 this.fireEvent('change', this, v, this.startValue);
12560 * Resets the current field value to the originally loaded value and clears any validation messages
12562 reset : function(){
12563 this.setValue(this.originalValue);
12567 * Returns the name of the field
12568 * @return {Mixed} name The name field
12570 getName: function(){
12574 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12575 * @return {Mixed} value The field value
12577 getValue : function(){
12579 var v = this.inputEl().getValue();
12584 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12585 * @return {Mixed} value The field value
12587 getRawValue : function(){
12588 var v = this.inputEl().getValue();
12594 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12595 * @param {Mixed} value The value to set
12597 setRawValue : function(v){
12598 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12601 selectText : function(start, end){
12602 var v = this.getRawValue();
12604 start = start === undefined ? 0 : start;
12605 end = end === undefined ? v.length : end;
12606 var d = this.inputEl().dom;
12607 if(d.setSelectionRange){
12608 d.setSelectionRange(start, end);
12609 }else if(d.createTextRange){
12610 var range = d.createTextRange();
12611 range.moveStart("character", start);
12612 range.moveEnd("character", v.length-end);
12619 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12620 * @param {Mixed} value The value to set
12622 setValue : function(v){
12625 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12631 processValue : function(value){
12632 if(this.stripCharsRe){
12633 var newValue = value.replace(this.stripCharsRe, '');
12634 if(newValue !== value){
12635 this.setRawValue(newValue);
12642 preFocus : function(){
12644 if(this.selectOnFocus){
12645 this.inputEl().dom.select();
12648 filterKeys : function(e){
12649 var k = e.getKey();
12650 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12653 var c = e.getCharCode(), cc = String.fromCharCode(c);
12654 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12657 if(!this.maskRe.test(cc)){
12662 * Clear any invalid styles/messages for this field
12664 clearInvalid : function(){
12666 if(!this.el || this.preventMark){ // not rendered
12671 this.el.removeClass([this.invalidClass, 'is-invalid']);
12673 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12675 var feedback = this.el.select('.form-control-feedback', true).first();
12678 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12683 if(this.indicator){
12684 this.indicator.removeClass('visible');
12685 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12688 this.fireEvent('valid', this);
12692 * Mark this field as valid
12694 markValid : function()
12696 if(!this.el || this.preventMark){ // not rendered...
12700 this.el.removeClass([this.invalidClass, this.validClass]);
12701 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12703 var feedback = this.el.select('.form-control-feedback', true).first();
12706 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12709 if(this.indicator){
12710 this.indicator.removeClass('visible');
12711 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12719 if(this.allowBlank && !this.getRawValue().length){
12722 if (Roo.bootstrap.version == 3) {
12723 this.el.addClass(this.validClass);
12725 this.inputEl().addClass('is-valid');
12728 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12730 var feedback = this.el.select('.form-control-feedback', true).first();
12733 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12734 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12739 this.fireEvent('valid', this);
12743 * Mark this field as invalid
12744 * @param {String} msg The validation message
12746 markInvalid : function(msg)
12748 if(!this.el || this.preventMark){ // not rendered
12752 this.el.removeClass([this.invalidClass, this.validClass]);
12753 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12755 var feedback = this.el.select('.form-control-feedback', true).first();
12758 this.el.select('.form-control-feedback', true).first().removeClass(
12759 [this.invalidFeedbackClass, this.validFeedbackClass]);
12766 if(this.allowBlank && !this.getRawValue().length){
12770 if(this.indicator){
12771 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12772 this.indicator.addClass('visible');
12774 if (Roo.bootstrap.version == 3) {
12775 this.el.addClass(this.invalidClass);
12777 this.inputEl().addClass('is-invalid');
12782 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12784 var feedback = this.el.select('.form-control-feedback', true).first();
12787 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12789 if(this.getValue().length || this.forceFeedback){
12790 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12797 this.fireEvent('invalid', this, msg);
12800 SafariOnKeyDown : function(event)
12802 // this is a workaround for a password hang bug on chrome/ webkit.
12803 if (this.inputEl().dom.type != 'password') {
12807 var isSelectAll = false;
12809 if(this.inputEl().dom.selectionEnd > 0){
12810 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12812 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12813 event.preventDefault();
12818 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12820 event.preventDefault();
12821 // this is very hacky as keydown always get's upper case.
12823 var cc = String.fromCharCode(event.getCharCode());
12824 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12828 adjustWidth : function(tag, w){
12829 tag = tag.toLowerCase();
12830 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12831 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12832 if(tag == 'input'){
12835 if(tag == 'textarea'){
12838 }else if(Roo.isOpera){
12839 if(tag == 'input'){
12842 if(tag == 'textarea'){
12850 setFieldLabel : function(v)
12852 if(!this.rendered){
12856 if(this.indicatorEl()){
12857 var ar = this.el.select('label > span',true);
12859 if (ar.elements.length) {
12860 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12861 this.fieldLabel = v;
12865 var br = this.el.select('label',true);
12867 if(br.elements.length) {
12868 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12869 this.fieldLabel = v;
12873 Roo.log('Cannot Found any of label > span || label in input');
12877 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12878 this.fieldLabel = v;
12893 * @class Roo.bootstrap.TextArea
12894 * @extends Roo.bootstrap.Input
12895 * Bootstrap TextArea class
12896 * @cfg {Number} cols Specifies the visible width of a text area
12897 * @cfg {Number} rows Specifies the visible number of lines in a text area
12898 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12899 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12900 * @cfg {string} html text
12903 * Create a new TextArea
12904 * @param {Object} config The config object
12907 Roo.bootstrap.TextArea = function(config){
12908 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12912 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12922 getAutoCreate : function(){
12924 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12930 if(this.inputType != 'hidden'){
12931 cfg.cls = 'form-group' //input-group
12939 value : this.value || '',
12940 html: this.html || '',
12941 cls : 'form-control',
12942 placeholder : this.placeholder || ''
12946 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12947 input.maxLength = this.maxLength;
12951 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12955 input.cols = this.cols;
12958 if (this.readOnly) {
12959 input.readonly = true;
12963 input.name = this.name;
12967 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12971 ['xs','sm','md','lg'].map(function(size){
12972 if (settings[size]) {
12973 cfg.cls += ' col-' + size + '-' + settings[size];
12977 var inputblock = input;
12979 if(this.hasFeedback && !this.allowBlank){
12983 cls: 'glyphicon form-control-feedback'
12987 cls : 'has-feedback',
12996 if (this.before || this.after) {
12999 cls : 'input-group',
13003 inputblock.cn.push({
13005 cls : 'input-group-addon',
13010 inputblock.cn.push(input);
13012 if(this.hasFeedback && !this.allowBlank){
13013 inputblock.cls += ' has-feedback';
13014 inputblock.cn.push(feedback);
13018 inputblock.cn.push({
13020 cls : 'input-group-addon',
13027 if (align ==='left' && this.fieldLabel.length) {
13032 cls : 'control-label',
13033 html : this.fieldLabel
13044 if(this.labelWidth > 12){
13045 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13048 if(this.labelWidth < 13 && this.labelmd == 0){
13049 this.labelmd = this.labelWidth;
13052 if(this.labellg > 0){
13053 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13054 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13057 if(this.labelmd > 0){
13058 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13059 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13062 if(this.labelsm > 0){
13063 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13064 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13067 if(this.labelxs > 0){
13068 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13069 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13072 } else if ( this.fieldLabel.length) {
13077 //cls : 'input-group-addon',
13078 html : this.fieldLabel
13096 if (this.disabled) {
13097 input.disabled=true;
13104 * return the real textarea element.
13106 inputEl: function ()
13108 return this.el.select('textarea.form-control',true).first();
13112 * Clear any invalid styles/messages for this field
13114 clearInvalid : function()
13117 if(!this.el || this.preventMark){ // not rendered
13121 var label = this.el.select('label', true).first();
13122 var icon = this.el.select('i.fa-star', true).first();
13127 this.el.removeClass( this.validClass);
13128 this.inputEl().removeClass('is-invalid');
13130 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13132 var feedback = this.el.select('.form-control-feedback', true).first();
13135 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13140 this.fireEvent('valid', this);
13144 * Mark this field as valid
13146 markValid : function()
13148 if(!this.el || this.preventMark){ // not rendered
13152 this.el.removeClass([this.invalidClass, this.validClass]);
13153 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13155 var feedback = this.el.select('.form-control-feedback', true).first();
13158 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13161 if(this.disabled || this.allowBlank){
13165 var label = this.el.select('label', true).first();
13166 var icon = this.el.select('i.fa-star', true).first();
13171 if (Roo.bootstrap.version == 3) {
13172 this.el.addClass(this.validClass);
13174 this.inputEl().addClass('is-valid');
13178 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13180 var feedback = this.el.select('.form-control-feedback', true).first();
13183 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13184 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13189 this.fireEvent('valid', this);
13193 * Mark this field as invalid
13194 * @param {String} msg The validation message
13196 markInvalid : function(msg)
13198 if(!this.el || this.preventMark){ // not rendered
13202 this.el.removeClass([this.invalidClass, this.validClass]);
13203 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13205 var feedback = this.el.select('.form-control-feedback', true).first();
13208 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13211 if(this.disabled || this.allowBlank){
13215 var label = this.el.select('label', true).first();
13216 var icon = this.el.select('i.fa-star', true).first();
13218 if(!this.getValue().length && label && !icon){
13219 this.el.createChild({
13221 cls : 'text-danger fa fa-lg fa-star',
13222 tooltip : 'This field is required',
13223 style : 'margin-right:5px;'
13227 if (Roo.bootstrap.version == 3) {
13228 this.el.addClass(this.invalidClass);
13230 this.inputEl().addClass('is-invalid');
13233 // fixme ... this may be depricated need to test..
13234 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13236 var feedback = this.el.select('.form-control-feedback', true).first();
13239 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13241 if(this.getValue().length || this.forceFeedback){
13242 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13249 this.fireEvent('invalid', this, msg);
13257 * trigger field - base class for combo..
13262 * @class Roo.bootstrap.TriggerField
13263 * @extends Roo.bootstrap.Input
13264 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13265 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13266 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13267 * for which you can provide a custom implementation. For example:
13269 var trigger = new Roo.bootstrap.TriggerField();
13270 trigger.onTriggerClick = myTriggerFn;
13271 trigger.applyTo('my-field');
13274 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13275 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13276 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13277 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13278 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13281 * Create a new TriggerField.
13282 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13283 * to the base TextField)
13285 Roo.bootstrap.TriggerField = function(config){
13286 this.mimicing = false;
13287 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13290 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13292 * @cfg {String} triggerClass A CSS class to apply to the trigger
13295 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13300 * @cfg {Boolean} removable (true|false) special filter default false
13304 /** @cfg {Boolean} grow @hide */
13305 /** @cfg {Number} growMin @hide */
13306 /** @cfg {Number} growMax @hide */
13312 autoSize: Roo.emptyFn,
13316 deferHeight : true,
13319 actionMode : 'wrap',
13324 getAutoCreate : function(){
13326 var align = this.labelAlign || this.parentLabelAlign();
13331 cls: 'form-group' //input-group
13338 type : this.inputType,
13339 cls : 'form-control',
13340 autocomplete: 'new-password',
13341 placeholder : this.placeholder || ''
13345 input.name = this.name;
13348 input.cls += ' input-' + this.size;
13351 if (this.disabled) {
13352 input.disabled=true;
13355 var inputblock = input;
13357 if(this.hasFeedback && !this.allowBlank){
13361 cls: 'glyphicon form-control-feedback'
13364 if(this.removable && !this.editable ){
13366 cls : 'has-feedback',
13372 cls : 'roo-combo-removable-btn close'
13379 cls : 'has-feedback',
13388 if(this.removable && !this.editable ){
13390 cls : 'roo-removable',
13396 cls : 'roo-combo-removable-btn close'
13403 if (this.before || this.after) {
13406 cls : 'input-group',
13410 inputblock.cn.push({
13412 cls : 'input-group-addon input-group-prepend input-group-text',
13417 inputblock.cn.push(input);
13419 if(this.hasFeedback && !this.allowBlank){
13420 inputblock.cls += ' has-feedback';
13421 inputblock.cn.push(feedback);
13425 inputblock.cn.push({
13427 cls : 'input-group-addon input-group-append input-group-text',
13436 var ibwrap = inputblock;
13441 cls: 'roo-select2-choices',
13445 cls: 'roo-select2-search-field',
13457 cls: 'roo-select2-container input-group',
13462 cls: 'form-hidden-field'
13468 if(!this.multiple && this.showToggleBtn){
13474 if (this.caret != false) {
13477 cls: 'fa fa-' + this.caret
13484 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13486 Roo.bootstrap.version == 3 ? caret : '',
13489 cls: 'combobox-clear',
13503 combobox.cls += ' roo-select2-container-multi';
13507 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13508 tooltip : 'This field is required'
13510 if (Roo.bootstrap.version == 4) {
13513 style : 'display:none'
13518 if (align ==='left' && this.fieldLabel.length) {
13520 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13527 cls : 'control-label',
13528 html : this.fieldLabel
13540 var labelCfg = cfg.cn[1];
13541 var contentCfg = cfg.cn[2];
13543 if(this.indicatorpos == 'right'){
13548 cls : 'control-label',
13552 html : this.fieldLabel
13566 labelCfg = cfg.cn[0];
13567 contentCfg = cfg.cn[1];
13570 if(this.labelWidth > 12){
13571 labelCfg.style = "width: " + this.labelWidth + 'px';
13574 if(this.labelWidth < 13 && this.labelmd == 0){
13575 this.labelmd = this.labelWidth;
13578 if(this.labellg > 0){
13579 labelCfg.cls += ' col-lg-' + this.labellg;
13580 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13583 if(this.labelmd > 0){
13584 labelCfg.cls += ' col-md-' + this.labelmd;
13585 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13588 if(this.labelsm > 0){
13589 labelCfg.cls += ' col-sm-' + this.labelsm;
13590 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13593 if(this.labelxs > 0){
13594 labelCfg.cls += ' col-xs-' + this.labelxs;
13595 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13598 } else if ( this.fieldLabel.length) {
13599 // Roo.log(" label");
13604 //cls : 'input-group-addon',
13605 html : this.fieldLabel
13613 if(this.indicatorpos == 'right'){
13621 html : this.fieldLabel
13635 // Roo.log(" no label && no align");
13642 ['xs','sm','md','lg'].map(function(size){
13643 if (settings[size]) {
13644 cfg.cls += ' col-' + size + '-' + settings[size];
13655 onResize : function(w, h){
13656 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13657 // if(typeof w == 'number'){
13658 // var x = w - this.trigger.getWidth();
13659 // this.inputEl().setWidth(this.adjustWidth('input', x));
13660 // this.trigger.setStyle('left', x+'px');
13665 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13668 getResizeEl : function(){
13669 return this.inputEl();
13673 getPositionEl : function(){
13674 return this.inputEl();
13678 alignErrorIcon : function(){
13679 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13683 initEvents : function(){
13687 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13688 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13689 if(!this.multiple && this.showToggleBtn){
13690 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13691 if(this.hideTrigger){
13692 this.trigger.setDisplayed(false);
13694 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13698 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13701 if(this.removable && !this.editable && !this.tickable){
13702 var close = this.closeTriggerEl();
13705 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13706 close.on('click', this.removeBtnClick, this, close);
13710 //this.trigger.addClassOnOver('x-form-trigger-over');
13711 //this.trigger.addClassOnClick('x-form-trigger-click');
13714 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13718 closeTriggerEl : function()
13720 var close = this.el.select('.roo-combo-removable-btn', true).first();
13721 return close ? close : false;
13724 removeBtnClick : function(e, h, el)
13726 e.preventDefault();
13728 if(this.fireEvent("remove", this) !== false){
13730 this.fireEvent("afterremove", this)
13734 createList : function()
13736 this.list = Roo.get(document.body).createChild({
13737 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13738 cls: 'typeahead typeahead-long dropdown-menu shadow',
13739 style: 'display:none'
13742 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13747 initTrigger : function(){
13752 onDestroy : function(){
13754 this.trigger.removeAllListeners();
13755 // this.trigger.remove();
13758 // this.wrap.remove();
13760 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13764 onFocus : function(){
13765 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13767 if(!this.mimicing){
13768 this.wrap.addClass('x-trigger-wrap-focus');
13769 this.mimicing = true;
13770 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13771 if(this.monitorTab){
13772 this.el.on("keydown", this.checkTab, this);
13779 checkTab : function(e){
13780 if(e.getKey() == e.TAB){
13781 this.triggerBlur();
13786 onBlur : function(){
13791 mimicBlur : function(e, t){
13793 if(!this.wrap.contains(t) && this.validateBlur()){
13794 this.triggerBlur();
13800 triggerBlur : function(){
13801 this.mimicing = false;
13802 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13803 if(this.monitorTab){
13804 this.el.un("keydown", this.checkTab, this);
13806 //this.wrap.removeClass('x-trigger-wrap-focus');
13807 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13811 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13812 validateBlur : function(e, t){
13817 onDisable : function(){
13818 this.inputEl().dom.disabled = true;
13819 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13821 // this.wrap.addClass('x-item-disabled');
13826 onEnable : function(){
13827 this.inputEl().dom.disabled = false;
13828 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13830 // this.el.removeClass('x-item-disabled');
13835 onShow : function(){
13836 var ae = this.getActionEl();
13839 ae.dom.style.display = '';
13840 ae.dom.style.visibility = 'visible';
13846 onHide : function(){
13847 var ae = this.getActionEl();
13848 ae.dom.style.display = 'none';
13852 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13853 * by an implementing function.
13855 * @param {EventObject} e
13857 onTriggerClick : Roo.emptyFn
13865 * @class Roo.bootstrap.CardUploader
13866 * @extends Roo.bootstrap.Button
13867 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13868 * @cfg {Number} errorTimeout default 3000
13869 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13870 * @cfg {Array} html The button text.
13874 * Create a new CardUploader
13875 * @param {Object} config The config object
13878 Roo.bootstrap.CardUploader = function(config){
13882 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13885 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13893 * When a image is clicked on - and needs to display a slideshow or similar..
13894 * @param {Roo.bootstrap.Card} this
13895 * @param {Object} The image information data
13901 * When a the download link is clicked
13902 * @param {Roo.bootstrap.Card} this
13903 * @param {Object} The image information data contains
13910 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13913 errorTimeout : 3000,
13917 fileCollection : false,
13920 getAutoCreate : function()
13924 cls :'form-group' ,
13929 //cls : 'input-group-addon',
13930 html : this.fieldLabel
13938 value : this.value,
13939 cls : 'd-none form-control'
13944 multiple : 'multiple',
13946 cls : 'd-none roo-card-upload-selector'
13950 cls : 'roo-card-uploader-button-container w-100 mb-2'
13953 cls : 'card-columns roo-card-uploader-container'
13963 getChildContainer : function() /// what children are added to.
13965 return this.containerEl;
13968 getButtonContainer : function() /// what children are added to.
13970 return this.el.select(".roo-card-uploader-button-container").first();
13973 initEvents : function()
13976 Roo.bootstrap.Input.prototype.initEvents.call(this);
13980 xns: Roo.bootstrap,
13983 container_method : 'getButtonContainer' ,
13984 html : this.html, // fix changable?
13987 'click' : function(btn, e) {
13996 this.urlAPI = (window.createObjectURL && window) ||
13997 (window.URL && URL.revokeObjectURL && URL) ||
13998 (window.webkitURL && webkitURL);
14003 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14005 this.selectorEl.on('change', this.onFileSelected, this);
14008 this.images.forEach(function(img) {
14011 this.images = false;
14013 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14019 onClick : function(e)
14021 e.preventDefault();
14023 this.selectorEl.dom.click();
14027 onFileSelected : function(e)
14029 e.preventDefault();
14031 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14035 Roo.each(this.selectorEl.dom.files, function(file){
14036 this.addFile(file);
14045 addFile : function(file)
14048 if(typeof(file) === 'string'){
14049 throw "Add file by name?"; // should not happen
14053 if(!file || !this.urlAPI){
14063 var url = _this.urlAPI.createObjectURL( file);
14066 id : Roo.bootstrap.CardUploader.ID--,
14067 is_uploaded : false,
14071 mimetype : file.type,
14079 * addCard - add an Attachment to the uploader
14080 * @param data - the data about the image to upload
14084 title : "Title of file",
14085 is_uploaded : false,
14086 src : "http://.....",
14087 srcfile : { the File upload object },
14088 mimetype : file.type,
14091 .. any other data...
14097 addCard : function (data)
14099 // hidden input element?
14100 // if the file is not an image...
14101 //then we need to use something other that and header_image
14106 xns : Roo.bootstrap,
14107 xtype : 'CardFooter',
14110 xns : Roo.bootstrap,
14116 xns : Roo.bootstrap,
14118 html : String.format("<small>{0}</small>", data.title),
14119 cls : 'col-10 text-left',
14124 click : function() {
14126 t.fireEvent( "download", t, data );
14132 xns : Roo.bootstrap,
14134 style: 'max-height: 28px; ',
14140 click : function() {
14141 t.removeCard(data.id)
14153 var cn = this.addxtype(
14156 xns : Roo.bootstrap,
14159 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14160 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14161 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14166 initEvents : function() {
14167 Roo.bootstrap.Card.prototype.initEvents.call(this);
14169 this.imgEl = this.el.select('.card-img-top').first();
14171 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14172 this.imgEl.set({ 'pointer' : 'cursor' });
14175 this.getCardFooter().addClass('p-1');
14182 // dont' really need ot update items.
14183 // this.items.push(cn);
14184 this.fileCollection.add(cn);
14186 if (!data.srcfile) {
14187 this.updateInput();
14192 var reader = new FileReader();
14193 reader.addEventListener("load", function() {
14194 data.srcdata = reader.result;
14197 reader.readAsDataURL(data.srcfile);
14202 removeCard : function(id)
14205 var card = this.fileCollection.get(id);
14206 card.data.is_deleted = 1;
14207 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14208 //this.fileCollection.remove(card);
14209 //this.items = this.items.filter(function(e) { return e != card });
14210 // dont' really need ot update items.
14211 card.el.dom.parentNode.removeChild(card.el.dom);
14212 this.updateInput();
14218 this.fileCollection.each(function(card) {
14219 if (card.el.dom && card.el.dom.parentNode) {
14220 card.el.dom.parentNode.removeChild(card.el.dom);
14223 this.fileCollection.clear();
14224 this.updateInput();
14227 updateInput : function()
14230 this.fileCollection.each(function(e) {
14234 this.inputEl().dom.value = JSON.stringify(data);
14244 Roo.bootstrap.CardUploader.ID = -1;/*
14246 * Ext JS Library 1.1.1
14247 * Copyright(c) 2006-2007, Ext JS, LLC.
14249 * Originally Released Under LGPL - original licence link has changed is not relivant.
14252 * <script type="text/javascript">
14257 * @class Roo.data.SortTypes
14259 * Defines the default sorting (casting?) comparison functions used when sorting data.
14261 Roo.data.SortTypes = {
14263 * Default sort that does nothing
14264 * @param {Mixed} s The value being converted
14265 * @return {Mixed} The comparison value
14267 none : function(s){
14272 * The regular expression used to strip tags
14276 stripTagsRE : /<\/?[^>]+>/gi,
14279 * Strips all HTML tags to sort on text only
14280 * @param {Mixed} s The value being converted
14281 * @return {String} The comparison value
14283 asText : function(s){
14284 return String(s).replace(this.stripTagsRE, "");
14288 * Strips all HTML tags to sort on text only - Case insensitive
14289 * @param {Mixed} s The value being converted
14290 * @return {String} The comparison value
14292 asUCText : function(s){
14293 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14297 * Case insensitive string
14298 * @param {Mixed} s The value being converted
14299 * @return {String} The comparison value
14301 asUCString : function(s) {
14302 return String(s).toUpperCase();
14307 * @param {Mixed} s The value being converted
14308 * @return {Number} The comparison value
14310 asDate : function(s) {
14314 if(s instanceof Date){
14315 return s.getTime();
14317 return Date.parse(String(s));
14322 * @param {Mixed} s The value being converted
14323 * @return {Float} The comparison value
14325 asFloat : function(s) {
14326 var val = parseFloat(String(s).replace(/,/g, ""));
14335 * @param {Mixed} s The value being converted
14336 * @return {Number} The comparison value
14338 asInt : function(s) {
14339 var val = parseInt(String(s).replace(/,/g, ""));
14347 * Ext JS Library 1.1.1
14348 * Copyright(c) 2006-2007, Ext JS, LLC.
14350 * Originally Released Under LGPL - original licence link has changed is not relivant.
14353 * <script type="text/javascript">
14357 * @class Roo.data.Record
14358 * Instances of this class encapsulate both record <em>definition</em> information, and record
14359 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14360 * to access Records cached in an {@link Roo.data.Store} object.<br>
14362 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14363 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14366 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14368 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14369 * {@link #create}. The parameters are the same.
14370 * @param {Array} data An associative Array of data values keyed by the field name.
14371 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14372 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14373 * not specified an integer id is generated.
14375 Roo.data.Record = function(data, id){
14376 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14381 * Generate a constructor for a specific record layout.
14382 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14383 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14384 * Each field definition object may contain the following properties: <ul>
14385 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14386 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14387 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14388 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14389 * is being used, then this is a string containing the javascript expression to reference the data relative to
14390 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14391 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14392 * this may be omitted.</p></li>
14393 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14394 * <ul><li>auto (Default, implies no conversion)</li>
14399 * <li>date</li></ul></p></li>
14400 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14401 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14402 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14403 * by the Reader into an object that will be stored in the Record. It is passed the
14404 * following parameters:<ul>
14405 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14407 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14409 * <br>usage:<br><pre><code>
14410 var TopicRecord = Roo.data.Record.create(
14411 {name: 'title', mapping: 'topic_title'},
14412 {name: 'author', mapping: 'username'},
14413 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14414 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14415 {name: 'lastPoster', mapping: 'user2'},
14416 {name: 'excerpt', mapping: 'post_text'}
14419 var myNewRecord = new TopicRecord({
14420 title: 'Do my job please',
14423 lastPost: new Date(),
14424 lastPoster: 'Animal',
14425 excerpt: 'No way dude!'
14427 myStore.add(myNewRecord);
14432 Roo.data.Record.create = function(o){
14433 var f = function(){
14434 f.superclass.constructor.apply(this, arguments);
14436 Roo.extend(f, Roo.data.Record);
14437 var p = f.prototype;
14438 p.fields = new Roo.util.MixedCollection(false, function(field){
14441 for(var i = 0, len = o.length; i < len; i++){
14442 p.fields.add(new Roo.data.Field(o[i]));
14444 f.getField = function(name){
14445 return p.fields.get(name);
14450 Roo.data.Record.AUTO_ID = 1000;
14451 Roo.data.Record.EDIT = 'edit';
14452 Roo.data.Record.REJECT = 'reject';
14453 Roo.data.Record.COMMIT = 'commit';
14455 Roo.data.Record.prototype = {
14457 * Readonly flag - true if this record has been modified.
14466 join : function(store){
14467 this.store = store;
14471 * Set the named field to the specified value.
14472 * @param {String} name The name of the field to set.
14473 * @param {Object} value The value to set the field to.
14475 set : function(name, value){
14476 if(this.data[name] == value){
14480 if(!this.modified){
14481 this.modified = {};
14483 if(typeof this.modified[name] == 'undefined'){
14484 this.modified[name] = this.data[name];
14486 this.data[name] = value;
14487 if(!this.editing && this.store){
14488 this.store.afterEdit(this);
14493 * Get the value of the named field.
14494 * @param {String} name The name of the field to get the value of.
14495 * @return {Object} The value of the field.
14497 get : function(name){
14498 return this.data[name];
14502 beginEdit : function(){
14503 this.editing = true;
14504 this.modified = {};
14508 cancelEdit : function(){
14509 this.editing = false;
14510 delete this.modified;
14514 endEdit : function(){
14515 this.editing = false;
14516 if(this.dirty && this.store){
14517 this.store.afterEdit(this);
14522 * Usually called by the {@link Roo.data.Store} which owns the Record.
14523 * Rejects all changes made to the Record since either creation, or the last commit operation.
14524 * Modified fields are reverted to their original values.
14526 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14527 * of reject operations.
14529 reject : function(){
14530 var m = this.modified;
14532 if(typeof m[n] != "function"){
14533 this.data[n] = m[n];
14536 this.dirty = false;
14537 delete this.modified;
14538 this.editing = false;
14540 this.store.afterReject(this);
14545 * Usually called by the {@link Roo.data.Store} which owns the Record.
14546 * Commits all changes made to the Record since either creation, or the last commit operation.
14548 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14549 * of commit operations.
14551 commit : function(){
14552 this.dirty = false;
14553 delete this.modified;
14554 this.editing = false;
14556 this.store.afterCommit(this);
14561 hasError : function(){
14562 return this.error != null;
14566 clearError : function(){
14571 * Creates a copy of this record.
14572 * @param {String} id (optional) A new record id if you don't want to use this record's id
14575 copy : function(newId) {
14576 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14580 * Ext JS Library 1.1.1
14581 * Copyright(c) 2006-2007, Ext JS, LLC.
14583 * Originally Released Under LGPL - original licence link has changed is not relivant.
14586 * <script type="text/javascript">
14592 * @class Roo.data.Store
14593 * @extends Roo.util.Observable
14594 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14595 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14597 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
14598 * has no knowledge of the format of the data returned by the Proxy.<br>
14600 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14601 * instances from the data object. These records are cached and made available through accessor functions.
14603 * Creates a new Store.
14604 * @param {Object} config A config object containing the objects needed for the Store to access data,
14605 * and read the data into Records.
14607 Roo.data.Store = function(config){
14608 this.data = new Roo.util.MixedCollection(false);
14609 this.data.getKey = function(o){
14612 this.baseParams = {};
14614 this.paramNames = {
14619 "multisort" : "_multisort"
14622 if(config && config.data){
14623 this.inlineData = config.data;
14624 delete config.data;
14627 Roo.apply(this, config);
14629 if(this.reader){ // reader passed
14630 this.reader = Roo.factory(this.reader, Roo.data);
14631 this.reader.xmodule = this.xmodule || false;
14632 if(!this.recordType){
14633 this.recordType = this.reader.recordType;
14635 if(this.reader.onMetaChange){
14636 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14640 if(this.recordType){
14641 this.fields = this.recordType.prototype.fields;
14643 this.modified = [];
14647 * @event datachanged
14648 * Fires when the data cache has changed, and a widget which is using this Store
14649 * as a Record cache should refresh its view.
14650 * @param {Store} this
14652 datachanged : true,
14654 * @event metachange
14655 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14656 * @param {Store} this
14657 * @param {Object} meta The JSON metadata
14662 * Fires when Records have been added to the Store
14663 * @param {Store} this
14664 * @param {Roo.data.Record[]} records The array of Records added
14665 * @param {Number} index The index at which the record(s) were added
14670 * Fires when a Record has been removed from the Store
14671 * @param {Store} this
14672 * @param {Roo.data.Record} record The Record that was removed
14673 * @param {Number} index The index at which the record was removed
14678 * Fires when a Record has been updated
14679 * @param {Store} this
14680 * @param {Roo.data.Record} record The Record that was updated
14681 * @param {String} operation The update operation being performed. Value may be one of:
14683 Roo.data.Record.EDIT
14684 Roo.data.Record.REJECT
14685 Roo.data.Record.COMMIT
14691 * Fires when the data cache has been cleared.
14692 * @param {Store} this
14696 * @event beforeload
14697 * Fires before a request is made for a new data object. If the beforeload handler returns false
14698 * the load action will be canceled.
14699 * @param {Store} this
14700 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14704 * @event beforeloadadd
14705 * Fires after a new set of Records has been loaded.
14706 * @param {Store} this
14707 * @param {Roo.data.Record[]} records The Records that were loaded
14708 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14710 beforeloadadd : true,
14713 * Fires after a new set of Records has been loaded, before they are added to the store.
14714 * @param {Store} this
14715 * @param {Roo.data.Record[]} records The Records that were loaded
14716 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14717 * @params {Object} return from reader
14721 * @event loadexception
14722 * Fires if an exception occurs in the Proxy during loading.
14723 * Called with the signature of the Proxy's "loadexception" event.
14724 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14727 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14728 * @param {Object} load options
14729 * @param {Object} jsonData from your request (normally this contains the Exception)
14731 loadexception : true
14735 this.proxy = Roo.factory(this.proxy, Roo.data);
14736 this.proxy.xmodule = this.xmodule || false;
14737 this.relayEvents(this.proxy, ["loadexception"]);
14739 this.sortToggle = {};
14740 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14742 Roo.data.Store.superclass.constructor.call(this);
14744 if(this.inlineData){
14745 this.loadData(this.inlineData);
14746 delete this.inlineData;
14750 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14752 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14753 * without a remote query - used by combo/forms at present.
14757 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14760 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14763 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14764 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14767 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14768 * on any HTTP request
14771 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14774 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14778 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14779 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14781 remoteSort : false,
14784 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14785 * loaded or when a record is removed. (defaults to false).
14787 pruneModifiedRecords : false,
14790 lastOptions : null,
14793 * Add Records to the Store and fires the add event.
14794 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14796 add : function(records){
14797 records = [].concat(records);
14798 for(var i = 0, len = records.length; i < len; i++){
14799 records[i].join(this);
14801 var index = this.data.length;
14802 this.data.addAll(records);
14803 this.fireEvent("add", this, records, index);
14807 * Remove a Record from the Store and fires the remove event.
14808 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14810 remove : function(record){
14811 var index = this.data.indexOf(record);
14812 this.data.removeAt(index);
14814 if(this.pruneModifiedRecords){
14815 this.modified.remove(record);
14817 this.fireEvent("remove", this, record, index);
14821 * Remove all Records from the Store and fires the clear event.
14823 removeAll : function(){
14825 if(this.pruneModifiedRecords){
14826 this.modified = [];
14828 this.fireEvent("clear", this);
14832 * Inserts Records to the Store at the given index and fires the add event.
14833 * @param {Number} index The start index at which to insert the passed Records.
14834 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14836 insert : function(index, records){
14837 records = [].concat(records);
14838 for(var i = 0, len = records.length; i < len; i++){
14839 this.data.insert(index, records[i]);
14840 records[i].join(this);
14842 this.fireEvent("add", this, records, index);
14846 * Get the index within the cache of the passed Record.
14847 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14848 * @return {Number} The index of the passed Record. Returns -1 if not found.
14850 indexOf : function(record){
14851 return this.data.indexOf(record);
14855 * Get the index within the cache of the Record with the passed id.
14856 * @param {String} id The id of the Record to find.
14857 * @return {Number} The index of the Record. Returns -1 if not found.
14859 indexOfId : function(id){
14860 return this.data.indexOfKey(id);
14864 * Get the Record with the specified id.
14865 * @param {String} id The id of the Record to find.
14866 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14868 getById : function(id){
14869 return this.data.key(id);
14873 * Get the Record at the specified index.
14874 * @param {Number} index The index of the Record to find.
14875 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14877 getAt : function(index){
14878 return this.data.itemAt(index);
14882 * Returns a range of Records between specified indices.
14883 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14884 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14885 * @return {Roo.data.Record[]} An array of Records
14887 getRange : function(start, end){
14888 return this.data.getRange(start, end);
14892 storeOptions : function(o){
14893 o = Roo.apply({}, o);
14896 this.lastOptions = o;
14900 * Loads the Record cache from the configured Proxy using the configured Reader.
14902 * If using remote paging, then the first load call must specify the <em>start</em>
14903 * and <em>limit</em> properties in the options.params property to establish the initial
14904 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14906 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14907 * and this call will return before the new data has been loaded. Perform any post-processing
14908 * in a callback function, or in a "load" event handler.</strong>
14910 * @param {Object} options An object containing properties which control loading options:<ul>
14911 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14912 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14913 * passed the following arguments:<ul>
14914 * <li>r : Roo.data.Record[]</li>
14915 * <li>options: Options object from the load call</li>
14916 * <li>success: Boolean success indicator</li></ul></li>
14917 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14918 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14921 load : function(options){
14922 options = options || {};
14923 if(this.fireEvent("beforeload", this, options) !== false){
14924 this.storeOptions(options);
14925 var p = Roo.apply(options.params || {}, this.baseParams);
14926 // if meta was not loaded from remote source.. try requesting it.
14927 if (!this.reader.metaFromRemote) {
14928 p._requestMeta = 1;
14930 if(this.sortInfo && this.remoteSort){
14931 var pn = this.paramNames;
14932 p[pn["sort"]] = this.sortInfo.field;
14933 p[pn["dir"]] = this.sortInfo.direction;
14935 if (this.multiSort) {
14936 var pn = this.paramNames;
14937 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14940 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14945 * Reloads the Record cache from the configured Proxy using the configured Reader and
14946 * the options from the last load operation performed.
14947 * @param {Object} options (optional) An object containing properties which may override the options
14948 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14949 * the most recently used options are reused).
14951 reload : function(options){
14952 this.load(Roo.applyIf(options||{}, this.lastOptions));
14956 // Called as a callback by the Reader during a load operation.
14957 loadRecords : function(o, options, success){
14958 if(!o || success === false){
14959 if(success !== false){
14960 this.fireEvent("load", this, [], options, o);
14962 if(options.callback){
14963 options.callback.call(options.scope || this, [], options, false);
14967 // if data returned failure - throw an exception.
14968 if (o.success === false) {
14969 // show a message if no listener is registered.
14970 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14971 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14973 // loadmask wil be hooked into this..
14974 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14977 var r = o.records, t = o.totalRecords || r.length;
14979 this.fireEvent("beforeloadadd", this, r, options, o);
14981 if(!options || options.add !== true){
14982 if(this.pruneModifiedRecords){
14983 this.modified = [];
14985 for(var i = 0, len = r.length; i < len; i++){
14989 this.data = this.snapshot;
14990 delete this.snapshot;
14993 this.data.addAll(r);
14994 this.totalLength = t;
14996 this.fireEvent("datachanged", this);
14998 this.totalLength = Math.max(t, this.data.length+r.length);
15002 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15004 var e = new Roo.data.Record({});
15006 e.set(this.parent.displayField, this.parent.emptyTitle);
15007 e.set(this.parent.valueField, '');
15012 this.fireEvent("load", this, r, options, o);
15013 if(options.callback){
15014 options.callback.call(options.scope || this, r, options, true);
15020 * Loads data from a passed data block. A Reader which understands the format of the data
15021 * must have been configured in the constructor.
15022 * @param {Object} data The data block from which to read the Records. The format of the data expected
15023 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15024 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15026 loadData : function(o, append){
15027 var r = this.reader.readRecords(o);
15028 this.loadRecords(r, {add: append}, true);
15032 * using 'cn' the nested child reader read the child array into it's child stores.
15033 * @param {Object} rec The record with a 'children array
15035 loadDataFromChildren : function(rec)
15037 this.loadData(this.reader.toLoadData(rec));
15042 * Gets the number of cached records.
15044 * <em>If using paging, this may not be the total size of the dataset. If the data object
15045 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15046 * the data set size</em>
15048 getCount : function(){
15049 return this.data.length || 0;
15053 * Gets the total number of records in the dataset as returned by the server.
15055 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15056 * the dataset size</em>
15058 getTotalCount : function(){
15059 return this.totalLength || 0;
15063 * Returns the sort state of the Store as an object with two properties:
15065 field {String} The name of the field by which the Records are sorted
15066 direction {String} The sort order, "ASC" or "DESC"
15069 getSortState : function(){
15070 return this.sortInfo;
15074 applySort : function(){
15075 if(this.sortInfo && !this.remoteSort){
15076 var s = this.sortInfo, f = s.field;
15077 var st = this.fields.get(f).sortType;
15078 var fn = function(r1, r2){
15079 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15080 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15082 this.data.sort(s.direction, fn);
15083 if(this.snapshot && this.snapshot != this.data){
15084 this.snapshot.sort(s.direction, fn);
15090 * Sets the default sort column and order to be used by the next load operation.
15091 * @param {String} fieldName The name of the field to sort by.
15092 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15094 setDefaultSort : function(field, dir){
15095 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15099 * Sort the Records.
15100 * If remote sorting is used, the sort is performed on the server, and the cache is
15101 * reloaded. If local sorting is used, the cache is sorted internally.
15102 * @param {String} fieldName The name of the field to sort by.
15103 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15105 sort : function(fieldName, dir){
15106 var f = this.fields.get(fieldName);
15108 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15110 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15111 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15116 this.sortToggle[f.name] = dir;
15117 this.sortInfo = {field: f.name, direction: dir};
15118 if(!this.remoteSort){
15120 this.fireEvent("datachanged", this);
15122 this.load(this.lastOptions);
15127 * Calls the specified function for each of the Records in the cache.
15128 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15129 * Returning <em>false</em> aborts and exits the iteration.
15130 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15132 each : function(fn, scope){
15133 this.data.each(fn, scope);
15137 * Gets all records modified since the last commit. Modified records are persisted across load operations
15138 * (e.g., during paging).
15139 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15141 getModifiedRecords : function(){
15142 return this.modified;
15146 createFilterFn : function(property, value, anyMatch){
15147 if(!value.exec){ // not a regex
15148 value = String(value);
15149 if(value.length == 0){
15152 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15154 return function(r){
15155 return value.test(r.data[property]);
15160 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15161 * @param {String} property A field on your records
15162 * @param {Number} start The record index to start at (defaults to 0)
15163 * @param {Number} end The last record index to include (defaults to length - 1)
15164 * @return {Number} The sum
15166 sum : function(property, start, end){
15167 var rs = this.data.items, v = 0;
15168 start = start || 0;
15169 end = (end || end === 0) ? end : rs.length-1;
15171 for(var i = start; i <= end; i++){
15172 v += (rs[i].data[property] || 0);
15178 * Filter the records by a specified property.
15179 * @param {String} field A field on your records
15180 * @param {String/RegExp} value Either a string that the field
15181 * should start with or a RegExp to test against the field
15182 * @param {Boolean} anyMatch True to match any part not just the beginning
15184 filter : function(property, value, anyMatch){
15185 var fn = this.createFilterFn(property, value, anyMatch);
15186 return fn ? this.filterBy(fn) : this.clearFilter();
15190 * Filter by a function. The specified function will be called with each
15191 * record in this data source. If the function returns true the record is included,
15192 * otherwise it is filtered.
15193 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15194 * @param {Object} scope (optional) The scope of the function (defaults to this)
15196 filterBy : function(fn, scope){
15197 this.snapshot = this.snapshot || this.data;
15198 this.data = this.queryBy(fn, scope||this);
15199 this.fireEvent("datachanged", this);
15203 * Query the records by a specified property.
15204 * @param {String} field A field on your records
15205 * @param {String/RegExp} value Either a string that the field
15206 * should start with or a RegExp to test against the field
15207 * @param {Boolean} anyMatch True to match any part not just the beginning
15208 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15210 query : function(property, value, anyMatch){
15211 var fn = this.createFilterFn(property, value, anyMatch);
15212 return fn ? this.queryBy(fn) : this.data.clone();
15216 * Query by a function. The specified function will be called with each
15217 * record in this data source. If the function returns true the record is included
15219 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15220 * @param {Object} scope (optional) The scope of the function (defaults to this)
15221 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15223 queryBy : function(fn, scope){
15224 var data = this.snapshot || this.data;
15225 return data.filterBy(fn, scope||this);
15229 * Collects unique values for a particular dataIndex from this store.
15230 * @param {String} dataIndex The property to collect
15231 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15232 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15233 * @return {Array} An array of the unique values
15235 collect : function(dataIndex, allowNull, bypassFilter){
15236 var d = (bypassFilter === true && this.snapshot) ?
15237 this.snapshot.items : this.data.items;
15238 var v, sv, r = [], l = {};
15239 for(var i = 0, len = d.length; i < len; i++){
15240 v = d[i].data[dataIndex];
15242 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15251 * Revert to a view of the Record cache with no filtering applied.
15252 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15254 clearFilter : function(suppressEvent){
15255 if(this.snapshot && this.snapshot != this.data){
15256 this.data = this.snapshot;
15257 delete this.snapshot;
15258 if(suppressEvent !== true){
15259 this.fireEvent("datachanged", this);
15265 afterEdit : function(record){
15266 if(this.modified.indexOf(record) == -1){
15267 this.modified.push(record);
15269 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15273 afterReject : function(record){
15274 this.modified.remove(record);
15275 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15279 afterCommit : function(record){
15280 this.modified.remove(record);
15281 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15285 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15286 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15288 commitChanges : function(){
15289 var m = this.modified.slice(0);
15290 this.modified = [];
15291 for(var i = 0, len = m.length; i < len; i++){
15297 * Cancel outstanding changes on all changed records.
15299 rejectChanges : function(){
15300 var m = this.modified.slice(0);
15301 this.modified = [];
15302 for(var i = 0, len = m.length; i < len; i++){
15307 onMetaChange : function(meta, rtype, o){
15308 this.recordType = rtype;
15309 this.fields = rtype.prototype.fields;
15310 delete this.snapshot;
15311 this.sortInfo = meta.sortInfo || this.sortInfo;
15312 this.modified = [];
15313 this.fireEvent('metachange', this, this.reader.meta);
15316 moveIndex : function(data, type)
15318 var index = this.indexOf(data);
15320 var newIndex = index + type;
15324 this.insert(newIndex, data);
15329 * Ext JS Library 1.1.1
15330 * Copyright(c) 2006-2007, Ext JS, LLC.
15332 * Originally Released Under LGPL - original licence link has changed is not relivant.
15335 * <script type="text/javascript">
15339 * @class Roo.data.SimpleStore
15340 * @extends Roo.data.Store
15341 * Small helper class to make creating Stores from Array data easier.
15342 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15343 * @cfg {Array} fields An array of field definition objects, or field name strings.
15344 * @cfg {Object} an existing reader (eg. copied from another store)
15345 * @cfg {Array} data The multi-dimensional array of data
15347 * @param {Object} config
15349 Roo.data.SimpleStore = function(config)
15351 Roo.data.SimpleStore.superclass.constructor.call(this, {
15353 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15356 Roo.data.Record.create(config.fields)
15358 proxy : new Roo.data.MemoryProxy(config.data)
15362 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15364 * Ext JS Library 1.1.1
15365 * Copyright(c) 2006-2007, Ext JS, LLC.
15367 * Originally Released Under LGPL - original licence link has changed is not relivant.
15370 * <script type="text/javascript">
15375 * @extends Roo.data.Store
15376 * @class Roo.data.JsonStore
15377 * Small helper class to make creating Stores for JSON data easier. <br/>
15379 var store = new Roo.data.JsonStore({
15380 url: 'get-images.php',
15382 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15385 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15386 * JsonReader and HttpProxy (unless inline data is provided).</b>
15387 * @cfg {Array} fields An array of field definition objects, or field name strings.
15389 * @param {Object} config
15391 Roo.data.JsonStore = function(c){
15392 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15393 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15394 reader: new Roo.data.JsonReader(c, c.fields)
15397 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15399 * Ext JS Library 1.1.1
15400 * Copyright(c) 2006-2007, Ext JS, LLC.
15402 * Originally Released Under LGPL - original licence link has changed is not relivant.
15405 * <script type="text/javascript">
15409 Roo.data.Field = function(config){
15410 if(typeof config == "string"){
15411 config = {name: config};
15413 Roo.apply(this, config);
15416 this.type = "auto";
15419 var st = Roo.data.SortTypes;
15420 // named sortTypes are supported, here we look them up
15421 if(typeof this.sortType == "string"){
15422 this.sortType = st[this.sortType];
15425 // set default sortType for strings and dates
15426 if(!this.sortType){
15429 this.sortType = st.asUCString;
15432 this.sortType = st.asDate;
15435 this.sortType = st.none;
15440 var stripRe = /[\$,%]/g;
15442 // prebuilt conversion function for this field, instead of
15443 // switching every time we're reading a value
15445 var cv, dateFormat = this.dateFormat;
15450 cv = function(v){ return v; };
15453 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15457 return v !== undefined && v !== null && v !== '' ?
15458 parseInt(String(v).replace(stripRe, ""), 10) : '';
15463 return v !== undefined && v !== null && v !== '' ?
15464 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15469 cv = function(v){ return v === true || v === "true" || v == 1; };
15476 if(v instanceof Date){
15480 if(dateFormat == "timestamp"){
15481 return new Date(v*1000);
15483 return Date.parseDate(v, dateFormat);
15485 var parsed = Date.parse(v);
15486 return parsed ? new Date(parsed) : null;
15495 Roo.data.Field.prototype = {
15503 * Ext JS Library 1.1.1
15504 * Copyright(c) 2006-2007, Ext JS, LLC.
15506 * Originally Released Under LGPL - original licence link has changed is not relivant.
15509 * <script type="text/javascript">
15512 // Base class for reading structured data from a data source. This class is intended to be
15513 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15516 * @class Roo.data.DataReader
15517 * Base class for reading structured data from a data source. This class is intended to be
15518 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15521 Roo.data.DataReader = function(meta, recordType){
15525 this.recordType = recordType instanceof Array ?
15526 Roo.data.Record.create(recordType) : recordType;
15529 Roo.data.DataReader.prototype = {
15532 readerType : 'Data',
15534 * Create an empty record
15535 * @param {Object} data (optional) - overlay some values
15536 * @return {Roo.data.Record} record created.
15538 newRow : function(d) {
15540 this.recordType.prototype.fields.each(function(c) {
15542 case 'int' : da[c.name] = 0; break;
15543 case 'date' : da[c.name] = new Date(); break;
15544 case 'float' : da[c.name] = 0.0; break;
15545 case 'boolean' : da[c.name] = false; break;
15546 default : da[c.name] = ""; break;
15550 return new this.recordType(Roo.apply(da, d));
15556 * Ext JS Library 1.1.1
15557 * Copyright(c) 2006-2007, Ext JS, LLC.
15559 * Originally Released Under LGPL - original licence link has changed is not relivant.
15562 * <script type="text/javascript">
15566 * @class Roo.data.DataProxy
15567 * @extends Roo.data.Observable
15568 * This class is an abstract base class for implementations which provide retrieval of
15569 * unformatted data objects.<br>
15571 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15572 * (of the appropriate type which knows how to parse the data object) to provide a block of
15573 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15575 * Custom implementations must implement the load method as described in
15576 * {@link Roo.data.HttpProxy#load}.
15578 Roo.data.DataProxy = function(){
15581 * @event beforeload
15582 * Fires before a network request is made to retrieve a data object.
15583 * @param {Object} This DataProxy object.
15584 * @param {Object} params The params parameter to the load function.
15589 * Fires before the load method's callback is called.
15590 * @param {Object} This DataProxy object.
15591 * @param {Object} o The data object.
15592 * @param {Object} arg The callback argument object passed to the load function.
15596 * @event loadexception
15597 * Fires if an Exception occurs during data retrieval.
15598 * @param {Object} This DataProxy object.
15599 * @param {Object} o The data object.
15600 * @param {Object} arg The callback argument object passed to the load function.
15601 * @param {Object} e The Exception.
15603 loadexception : true
15605 Roo.data.DataProxy.superclass.constructor.call(this);
15608 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15611 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15615 * Ext JS Library 1.1.1
15616 * Copyright(c) 2006-2007, Ext JS, LLC.
15618 * Originally Released Under LGPL - original licence link has changed is not relivant.
15621 * <script type="text/javascript">
15624 * @class Roo.data.MemoryProxy
15625 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15626 * to the Reader when its load method is called.
15628 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15630 Roo.data.MemoryProxy = function(data){
15634 Roo.data.MemoryProxy.superclass.constructor.call(this);
15638 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15641 * Load data from the requested source (in this case an in-memory
15642 * data object passed to the constructor), read the data object into
15643 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15644 * process that block using the passed callback.
15645 * @param {Object} params This parameter is not used by the MemoryProxy class.
15646 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15647 * object into a block of Roo.data.Records.
15648 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15649 * The function must be passed <ul>
15650 * <li>The Record block object</li>
15651 * <li>The "arg" argument from the load function</li>
15652 * <li>A boolean success indicator</li>
15654 * @param {Object} scope The scope in which to call the callback
15655 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15657 load : function(params, reader, callback, scope, arg){
15658 params = params || {};
15661 result = reader.readRecords(params.data ? params.data :this.data);
15663 this.fireEvent("loadexception", this, arg, null, e);
15664 callback.call(scope, null, arg, false);
15667 callback.call(scope, result, arg, true);
15671 update : function(params, records){
15676 * Ext JS Library 1.1.1
15677 * Copyright(c) 2006-2007, Ext JS, LLC.
15679 * Originally Released Under LGPL - original licence link has changed is not relivant.
15682 * <script type="text/javascript">
15685 * @class Roo.data.HttpProxy
15686 * @extends Roo.data.DataProxy
15687 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15688 * configured to reference a certain URL.<br><br>
15690 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15691 * from which the running page was served.<br><br>
15693 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15695 * Be aware that to enable the browser to parse an XML document, the server must set
15696 * the Content-Type header in the HTTP response to "text/xml".
15698 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15699 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15700 * will be used to make the request.
15702 Roo.data.HttpProxy = function(conn){
15703 Roo.data.HttpProxy.superclass.constructor.call(this);
15704 // is conn a conn config or a real conn?
15706 this.useAjax = !conn || !conn.events;
15710 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15711 // thse are take from connection...
15714 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15717 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15718 * extra parameters to each request made by this object. (defaults to undefined)
15721 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15722 * to each request made by this object. (defaults to undefined)
15725 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
15728 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15731 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15737 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15741 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15742 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15743 * a finer-grained basis than the DataProxy events.
15745 getConnection : function(){
15746 return this.useAjax ? Roo.Ajax : this.conn;
15750 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15751 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15752 * process that block using the passed callback.
15753 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15754 * for the request to the remote server.
15755 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15756 * object into a block of Roo.data.Records.
15757 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15758 * The function must be passed <ul>
15759 * <li>The Record block object</li>
15760 * <li>The "arg" argument from the load function</li>
15761 * <li>A boolean success indicator</li>
15763 * @param {Object} scope The scope in which to call the callback
15764 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15766 load : function(params, reader, callback, scope, arg){
15767 if(this.fireEvent("beforeload", this, params) !== false){
15769 params : params || {},
15771 callback : callback,
15776 callback : this.loadResponse,
15780 Roo.applyIf(o, this.conn);
15781 if(this.activeRequest){
15782 Roo.Ajax.abort(this.activeRequest);
15784 this.activeRequest = Roo.Ajax.request(o);
15786 this.conn.request(o);
15789 callback.call(scope||this, null, arg, false);
15794 loadResponse : function(o, success, response){
15795 delete this.activeRequest;
15797 this.fireEvent("loadexception", this, o, response);
15798 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15803 result = o.reader.read(response);
15805 this.fireEvent("loadexception", this, o, response, e);
15806 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15810 this.fireEvent("load", this, o, o.request.arg);
15811 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15815 update : function(dataSet){
15820 updateResponse : function(dataSet){
15825 * Ext JS Library 1.1.1
15826 * Copyright(c) 2006-2007, Ext JS, LLC.
15828 * Originally Released Under LGPL - original licence link has changed is not relivant.
15831 * <script type="text/javascript">
15835 * @class Roo.data.ScriptTagProxy
15836 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15837 * other than the originating domain of the running page.<br><br>
15839 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
15840 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15842 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15843 * source code that is used as the source inside a <script> tag.<br><br>
15845 * In order for the browser to process the returned data, the server must wrap the data object
15846 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15847 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15848 * depending on whether the callback name was passed:
15851 boolean scriptTag = false;
15852 String cb = request.getParameter("callback");
15855 response.setContentType("text/javascript");
15857 response.setContentType("application/x-json");
15859 Writer out = response.getWriter();
15861 out.write(cb + "(");
15863 out.print(dataBlock.toJsonString());
15870 * @param {Object} config A configuration object.
15872 Roo.data.ScriptTagProxy = function(config){
15873 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15874 Roo.apply(this, config);
15875 this.head = document.getElementsByTagName("head")[0];
15878 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15880 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15882 * @cfg {String} url The URL from which to request the data object.
15885 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15889 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15890 * the server the name of the callback function set up by the load call to process the returned data object.
15891 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15892 * javascript output which calls this named function passing the data object as its only parameter.
15894 callbackParam : "callback",
15896 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15897 * name to the request.
15902 * Load data from the configured URL, read the data object into
15903 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15904 * process that block using the passed callback.
15905 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15906 * for the request to the remote server.
15907 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15908 * object into a block of Roo.data.Records.
15909 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15910 * The function must be passed <ul>
15911 * <li>The Record block object</li>
15912 * <li>The "arg" argument from the load function</li>
15913 * <li>A boolean success indicator</li>
15915 * @param {Object} scope The scope in which to call the callback
15916 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15918 load : function(params, reader, callback, scope, arg){
15919 if(this.fireEvent("beforeload", this, params) !== false){
15921 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15923 var url = this.url;
15924 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15926 url += "&_dc=" + (new Date().getTime());
15928 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15931 cb : "stcCallback"+transId,
15932 scriptId : "stcScript"+transId,
15936 callback : callback,
15942 window[trans.cb] = function(o){
15943 conn.handleResponse(o, trans);
15946 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15948 if(this.autoAbort !== false){
15952 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15954 var script = document.createElement("script");
15955 script.setAttribute("src", url);
15956 script.setAttribute("type", "text/javascript");
15957 script.setAttribute("id", trans.scriptId);
15958 this.head.appendChild(script);
15960 this.trans = trans;
15962 callback.call(scope||this, null, arg, false);
15967 isLoading : function(){
15968 return this.trans ? true : false;
15972 * Abort the current server request.
15974 abort : function(){
15975 if(this.isLoading()){
15976 this.destroyTrans(this.trans);
15981 destroyTrans : function(trans, isLoaded){
15982 this.head.removeChild(document.getElementById(trans.scriptId));
15983 clearTimeout(trans.timeoutId);
15985 window[trans.cb] = undefined;
15987 delete window[trans.cb];
15990 // if hasn't been loaded, wait for load to remove it to prevent script error
15991 window[trans.cb] = function(){
15992 window[trans.cb] = undefined;
15994 delete window[trans.cb];
16001 handleResponse : function(o, trans){
16002 this.trans = false;
16003 this.destroyTrans(trans, true);
16006 result = trans.reader.readRecords(o);
16008 this.fireEvent("loadexception", this, o, trans.arg, e);
16009 trans.callback.call(trans.scope||window, null, trans.arg, false);
16012 this.fireEvent("load", this, o, trans.arg);
16013 trans.callback.call(trans.scope||window, result, trans.arg, true);
16017 handleFailure : function(trans){
16018 this.trans = false;
16019 this.destroyTrans(trans, false);
16020 this.fireEvent("loadexception", this, null, trans.arg);
16021 trans.callback.call(trans.scope||window, null, trans.arg, false);
16025 * Ext JS Library 1.1.1
16026 * Copyright(c) 2006-2007, Ext JS, LLC.
16028 * Originally Released Under LGPL - original licence link has changed is not relivant.
16031 * <script type="text/javascript">
16035 * @class Roo.data.JsonReader
16036 * @extends Roo.data.DataReader
16037 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16038 * based on mappings in a provided Roo.data.Record constructor.
16040 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16041 * in the reply previously.
16046 var RecordDef = Roo.data.Record.create([
16047 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16048 {name: 'occupation'} // This field will use "occupation" as the mapping.
16050 var myReader = new Roo.data.JsonReader({
16051 totalProperty: "results", // The property which contains the total dataset size (optional)
16052 root: "rows", // The property which contains an Array of row objects
16053 id: "id" // The property within each row object that provides an ID for the record (optional)
16057 * This would consume a JSON file like this:
16059 { 'results': 2, 'rows': [
16060 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16061 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16064 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16065 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16066 * paged from the remote server.
16067 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16068 * @cfg {String} root name of the property which contains the Array of row objects.
16069 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16070 * @cfg {Array} fields Array of field definition objects
16072 * Create a new JsonReader
16073 * @param {Object} meta Metadata configuration options
16074 * @param {Object} recordType Either an Array of field definition objects,
16075 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16077 Roo.data.JsonReader = function(meta, recordType){
16080 // set some defaults:
16081 Roo.applyIf(meta, {
16082 totalProperty: 'total',
16083 successProperty : 'success',
16088 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16090 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16092 readerType : 'Json',
16095 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16096 * Used by Store query builder to append _requestMeta to params.
16099 metaFromRemote : false,
16101 * This method is only used by a DataProxy which has retrieved data from a remote server.
16102 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16103 * @return {Object} data A data block which is used by an Roo.data.Store object as
16104 * a cache of Roo.data.Records.
16106 read : function(response){
16107 var json = response.responseText;
16109 var o = /* eval:var:o */ eval("("+json+")");
16111 throw {message: "JsonReader.read: Json object not found"};
16117 this.metaFromRemote = true;
16118 this.meta = o.metaData;
16119 this.recordType = Roo.data.Record.create(o.metaData.fields);
16120 this.onMetaChange(this.meta, this.recordType, o);
16122 return this.readRecords(o);
16125 // private function a store will implement
16126 onMetaChange : function(meta, recordType, o){
16133 simpleAccess: function(obj, subsc) {
16140 getJsonAccessor: function(){
16142 return function(expr) {
16144 return(re.test(expr))
16145 ? new Function("obj", "return obj." + expr)
16150 return Roo.emptyFn;
16155 * Create a data block containing Roo.data.Records from an XML document.
16156 * @param {Object} o An object which contains an Array of row objects in the property specified
16157 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16158 * which contains the total size of the dataset.
16159 * @return {Object} data A data block which is used by an Roo.data.Store object as
16160 * a cache of Roo.data.Records.
16162 readRecords : function(o){
16164 * After any data loads, the raw JSON data is available for further custom processing.
16168 var s = this.meta, Record = this.recordType,
16169 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16171 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16173 if(s.totalProperty) {
16174 this.getTotal = this.getJsonAccessor(s.totalProperty);
16176 if(s.successProperty) {
16177 this.getSuccess = this.getJsonAccessor(s.successProperty);
16179 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16181 var g = this.getJsonAccessor(s.id);
16182 this.getId = function(rec) {
16184 return (r === undefined || r === "") ? null : r;
16187 this.getId = function(){return null;};
16190 for(var jj = 0; jj < fl; jj++){
16192 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16193 this.ef[jj] = this.getJsonAccessor(map);
16197 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16198 if(s.totalProperty){
16199 var vt = parseInt(this.getTotal(o), 10);
16204 if(s.successProperty){
16205 var vs = this.getSuccess(o);
16206 if(vs === false || vs === 'false'){
16211 for(var i = 0; i < c; i++){
16214 var id = this.getId(n);
16215 for(var j = 0; j < fl; j++){
16217 var v = this.ef[j](n);
16219 Roo.log('missing convert for ' + f.name);
16223 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16225 var record = new Record(values, id);
16227 records[i] = record;
16233 totalRecords : totalRecords
16236 // used when loading children.. @see loadDataFromChildren
16237 toLoadData: function(rec)
16239 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16240 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16241 return { data : data, total : data.length };
16246 * Ext JS Library 1.1.1
16247 * Copyright(c) 2006-2007, Ext JS, LLC.
16249 * Originally Released Under LGPL - original licence link has changed is not relivant.
16252 * <script type="text/javascript">
16256 * @class Roo.data.ArrayReader
16257 * @extends Roo.data.DataReader
16258 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16259 * Each element of that Array represents a row of data fields. The
16260 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16261 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16265 var RecordDef = Roo.data.Record.create([
16266 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16267 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16269 var myReader = new Roo.data.ArrayReader({
16270 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16274 * This would consume an Array like this:
16276 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16280 * Create a new JsonReader
16281 * @param {Object} meta Metadata configuration options.
16282 * @param {Object|Array} recordType Either an Array of field definition objects
16284 * @cfg {Array} fields Array of field definition objects
16285 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16286 * as specified to {@link Roo.data.Record#create},
16287 * or an {@link Roo.data.Record} object
16290 * created using {@link Roo.data.Record#create}.
16292 Roo.data.ArrayReader = function(meta, recordType)
16294 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16297 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16300 * Create a data block containing Roo.data.Records from an XML document.
16301 * @param {Object} o An Array of row objects which represents the dataset.
16302 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16303 * a cache of Roo.data.Records.
16305 readRecords : function(o)
16307 var sid = this.meta ? this.meta.id : null;
16308 var recordType = this.recordType, fields = recordType.prototype.fields;
16311 for(var i = 0; i < root.length; i++){
16314 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16315 for(var j = 0, jlen = fields.length; j < jlen; j++){
16316 var f = fields.items[j];
16317 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16318 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16320 values[f.name] = v;
16322 var record = new recordType(values, id);
16324 records[records.length] = record;
16328 totalRecords : records.length
16331 // used when loading children.. @see loadDataFromChildren
16332 toLoadData: function(rec)
16334 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16335 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16346 * @class Roo.bootstrap.ComboBox
16347 * @extends Roo.bootstrap.TriggerField
16348 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16349 * @cfg {Boolean} append (true|false) default false
16350 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16351 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16352 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16353 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16354 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16355 * @cfg {Boolean} animate default true
16356 * @cfg {Boolean} emptyResultText only for touch device
16357 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16358 * @cfg {String} emptyTitle default ''
16359 * @cfg {Number} width fixed with? experimental
16361 * Create a new ComboBox.
16362 * @param {Object} config Configuration options
16364 Roo.bootstrap.ComboBox = function(config){
16365 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16369 * Fires when the dropdown list is expanded
16370 * @param {Roo.bootstrap.ComboBox} combo This combo box
16375 * Fires when the dropdown list is collapsed
16376 * @param {Roo.bootstrap.ComboBox} combo This combo box
16380 * @event beforeselect
16381 * Fires before a list item is selected. Return false to cancel the selection.
16382 * @param {Roo.bootstrap.ComboBox} combo This combo box
16383 * @param {Roo.data.Record} record The data record returned from the underlying store
16384 * @param {Number} index The index of the selected item in the dropdown list
16386 'beforeselect' : true,
16389 * Fires when a list item is selected
16390 * @param {Roo.bootstrap.ComboBox} combo This combo box
16391 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16392 * @param {Number} index The index of the selected item in the dropdown list
16396 * @event beforequery
16397 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16398 * The event object passed has these properties:
16399 * @param {Roo.bootstrap.ComboBox} combo This combo box
16400 * @param {String} query The query
16401 * @param {Boolean} forceAll true to force "all" query
16402 * @param {Boolean} cancel true to cancel the query
16403 * @param {Object} e The query event object
16405 'beforequery': true,
16408 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16409 * @param {Roo.bootstrap.ComboBox} combo This combo box
16414 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16415 * @param {Roo.bootstrap.ComboBox} combo This combo box
16416 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16421 * Fires when the remove value from the combobox array
16422 * @param {Roo.bootstrap.ComboBox} combo This combo box
16426 * @event afterremove
16427 * Fires when the remove value from the combobox array
16428 * @param {Roo.bootstrap.ComboBox} combo This combo box
16430 'afterremove' : true,
16432 * @event specialfilter
16433 * Fires when specialfilter
16434 * @param {Roo.bootstrap.ComboBox} combo This combo box
16436 'specialfilter' : true,
16439 * Fires when tick the element
16440 * @param {Roo.bootstrap.ComboBox} combo This combo box
16444 * @event touchviewdisplay
16445 * Fires when touch view require special display (default is using displayField)
16446 * @param {Roo.bootstrap.ComboBox} combo This combo box
16447 * @param {Object} cfg set html .
16449 'touchviewdisplay' : true
16454 this.tickItems = [];
16456 this.selectedIndex = -1;
16457 if(this.mode == 'local'){
16458 if(config.queryDelay === undefined){
16459 this.queryDelay = 10;
16461 if(config.minChars === undefined){
16467 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16470 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16471 * rendering into an Roo.Editor, defaults to false)
16474 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16475 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16478 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16481 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16482 * the dropdown list (defaults to undefined, with no header element)
16486 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16490 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16492 listWidth: undefined,
16494 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16495 * mode = 'remote' or 'text' if mode = 'local')
16497 displayField: undefined,
16500 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16501 * mode = 'remote' or 'value' if mode = 'local').
16502 * Note: use of a valueField requires the user make a selection
16503 * in order for a value to be mapped.
16505 valueField: undefined,
16507 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16512 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16513 * field's data value (defaults to the underlying DOM element's name)
16515 hiddenName: undefined,
16517 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16521 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16523 selectedClass: 'active',
16526 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16530 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16531 * anchor positions (defaults to 'tl-bl')
16533 listAlign: 'tl-bl?',
16535 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16539 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16540 * query specified by the allQuery config option (defaults to 'query')
16542 triggerAction: 'query',
16544 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16545 * (defaults to 4, does not apply if editable = false)
16549 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16550 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16554 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16555 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16559 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16560 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16564 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16565 * when editable = true (defaults to false)
16567 selectOnFocus:false,
16569 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16571 queryParam: 'query',
16573 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16574 * when mode = 'remote' (defaults to 'Loading...')
16576 loadingText: 'Loading...',
16578 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16582 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16586 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16587 * traditional select (defaults to true)
16591 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16595 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16599 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16600 * listWidth has a higher value)
16604 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16605 * allow the user to set arbitrary text into the field (defaults to false)
16607 forceSelection:false,
16609 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16610 * if typeAhead = true (defaults to 250)
16612 typeAheadDelay : 250,
16614 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16615 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16617 valueNotFoundText : undefined,
16619 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16621 blockFocus : false,
16624 * @cfg {Boolean} disableClear Disable showing of clear button.
16626 disableClear : false,
16628 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16630 alwaysQuery : false,
16633 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16638 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16640 invalidClass : "has-warning",
16643 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16645 validClass : "has-success",
16648 * @cfg {Boolean} specialFilter (true|false) special filter default false
16650 specialFilter : false,
16653 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16655 mobileTouchView : true,
16658 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16660 useNativeIOS : false,
16663 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16665 mobile_restrict_height : false,
16667 ios_options : false,
16679 btnPosition : 'right',
16680 triggerList : true,
16681 showToggleBtn : true,
16683 emptyResultText: 'Empty',
16684 triggerText : 'Select',
16688 // element that contains real text value.. (when hidden is used..)
16690 getAutoCreate : function()
16695 * Render classic select for iso
16698 if(Roo.isIOS && this.useNativeIOS){
16699 cfg = this.getAutoCreateNativeIOS();
16707 if(Roo.isTouch && this.mobileTouchView){
16708 cfg = this.getAutoCreateTouchView();
16715 if(!this.tickable){
16716 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16721 * ComboBox with tickable selections
16724 var align = this.labelAlign || this.parentLabelAlign();
16727 cls : 'form-group roo-combobox-tickable' //input-group
16730 var btn_text_select = '';
16731 var btn_text_done = '';
16732 var btn_text_cancel = '';
16734 if (this.btn_text_show) {
16735 btn_text_select = 'Select';
16736 btn_text_done = 'Done';
16737 btn_text_cancel = 'Cancel';
16742 cls : 'tickable-buttons',
16747 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16748 //html : this.triggerText
16749 html: btn_text_select
16755 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16757 html: btn_text_done
16763 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16765 html: btn_text_cancel
16771 buttons.cn.unshift({
16773 cls: 'roo-select2-search-field-input'
16779 Roo.each(buttons.cn, function(c){
16781 c.cls += ' btn-' + _this.size;
16784 if (_this.disabled) {
16791 style : 'display: contents',
16796 cls: 'form-hidden-field'
16800 cls: 'roo-select2-choices',
16804 cls: 'roo-select2-search-field',
16815 cls: 'roo-select2-container input-group roo-select2-container-multi',
16821 // cls: 'typeahead typeahead-long dropdown-menu',
16822 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16827 if(this.hasFeedback && !this.allowBlank){
16831 cls: 'glyphicon form-control-feedback'
16834 combobox.cn.push(feedback);
16841 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16842 tooltip : 'This field is required'
16844 if (Roo.bootstrap.version == 4) {
16847 style : 'display:none'
16850 if (align ==='left' && this.fieldLabel.length) {
16852 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16859 cls : 'control-label col-form-label',
16860 html : this.fieldLabel
16872 var labelCfg = cfg.cn[1];
16873 var contentCfg = cfg.cn[2];
16876 if(this.indicatorpos == 'right'){
16882 cls : 'control-label col-form-label',
16886 html : this.fieldLabel
16902 labelCfg = cfg.cn[0];
16903 contentCfg = cfg.cn[1];
16907 if(this.labelWidth > 12){
16908 labelCfg.style = "width: " + this.labelWidth + 'px';
16910 if(this.width * 1 > 0){
16911 contentCfg.style = "width: " + this.width + 'px';
16913 if(this.labelWidth < 13 && this.labelmd == 0){
16914 this.labelmd = this.labelWidth;
16917 if(this.labellg > 0){
16918 labelCfg.cls += ' col-lg-' + this.labellg;
16919 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16922 if(this.labelmd > 0){
16923 labelCfg.cls += ' col-md-' + this.labelmd;
16924 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16927 if(this.labelsm > 0){
16928 labelCfg.cls += ' col-sm-' + this.labelsm;
16929 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16932 if(this.labelxs > 0){
16933 labelCfg.cls += ' col-xs-' + this.labelxs;
16934 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16938 } else if ( this.fieldLabel.length) {
16939 // Roo.log(" label");
16944 //cls : 'input-group-addon',
16945 html : this.fieldLabel
16950 if(this.indicatorpos == 'right'){
16954 //cls : 'input-group-addon',
16955 html : this.fieldLabel
16965 // Roo.log(" no label && no align");
16972 ['xs','sm','md','lg'].map(function(size){
16973 if (settings[size]) {
16974 cfg.cls += ' col-' + size + '-' + settings[size];
16982 _initEventsCalled : false,
16985 initEvents: function()
16987 if (this._initEventsCalled) { // as we call render... prevent looping...
16990 this._initEventsCalled = true;
16993 throw "can not find store for combo";
16996 this.indicator = this.indicatorEl();
16998 this.store = Roo.factory(this.store, Roo.data);
16999 this.store.parent = this;
17001 // if we are building from html. then this element is so complex, that we can not really
17002 // use the rendered HTML.
17003 // so we have to trash and replace the previous code.
17004 if (Roo.XComponent.build_from_html) {
17005 // remove this element....
17006 var e = this.el.dom, k=0;
17007 while (e ) { e = e.previousSibling; ++k;}
17012 this.rendered = false;
17014 this.render(this.parent().getChildContainer(true), k);
17017 if(Roo.isIOS && this.useNativeIOS){
17018 this.initIOSView();
17026 if(Roo.isTouch && this.mobileTouchView){
17027 this.initTouchView();
17032 this.initTickableEvents();
17036 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17038 if(this.hiddenName){
17040 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17042 this.hiddenField.dom.value =
17043 this.hiddenValue !== undefined ? this.hiddenValue :
17044 this.value !== undefined ? this.value : '';
17046 // prevent input submission
17047 this.el.dom.removeAttribute('name');
17048 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17053 // this.el.dom.setAttribute('autocomplete', 'off');
17056 var cls = 'x-combo-list';
17058 //this.list = new Roo.Layer({
17059 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17065 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17066 _this.list.setWidth(lw);
17069 this.list.on('mouseover', this.onViewOver, this);
17070 this.list.on('mousemove', this.onViewMove, this);
17071 this.list.on('scroll', this.onViewScroll, this);
17074 this.list.swallowEvent('mousewheel');
17075 this.assetHeight = 0;
17078 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17079 this.assetHeight += this.header.getHeight();
17082 this.innerList = this.list.createChild({cls:cls+'-inner'});
17083 this.innerList.on('mouseover', this.onViewOver, this);
17084 this.innerList.on('mousemove', this.onViewMove, this);
17085 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17087 if(this.allowBlank && !this.pageSize && !this.disableClear){
17088 this.footer = this.list.createChild({cls:cls+'-ft'});
17089 this.pageTb = new Roo.Toolbar(this.footer);
17093 this.footer = this.list.createChild({cls:cls+'-ft'});
17094 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17095 {pageSize: this.pageSize});
17099 if (this.pageTb && this.allowBlank && !this.disableClear) {
17101 this.pageTb.add(new Roo.Toolbar.Fill(), {
17102 cls: 'x-btn-icon x-btn-clear',
17104 handler: function()
17107 _this.clearValue();
17108 _this.onSelect(false, -1);
17113 this.assetHeight += this.footer.getHeight();
17118 this.tpl = Roo.bootstrap.version == 4 ?
17119 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17120 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17123 this.view = new Roo.View(this.list, this.tpl, {
17124 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17126 //this.view.wrapEl.setDisplayed(false);
17127 this.view.on('click', this.onViewClick, this);
17130 this.store.on('beforeload', this.onBeforeLoad, this);
17131 this.store.on('load', this.onLoad, this);
17132 this.store.on('loadexception', this.onLoadException, this);
17134 if(this.resizable){
17135 this.resizer = new Roo.Resizable(this.list, {
17136 pinned:true, handles:'se'
17138 this.resizer.on('resize', function(r, w, h){
17139 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17140 this.listWidth = w;
17141 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17142 this.restrictHeight();
17144 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17147 if(!this.editable){
17148 this.editable = true;
17149 this.setEditable(false);
17154 if (typeof(this.events.add.listeners) != 'undefined') {
17156 this.addicon = this.wrap.createChild(
17157 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17159 this.addicon.on('click', function(e) {
17160 this.fireEvent('add', this);
17163 if (typeof(this.events.edit.listeners) != 'undefined') {
17165 this.editicon = this.wrap.createChild(
17166 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17167 if (this.addicon) {
17168 this.editicon.setStyle('margin-left', '40px');
17170 this.editicon.on('click', function(e) {
17172 // we fire even if inothing is selected..
17173 this.fireEvent('edit', this, this.lastData );
17179 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17180 "up" : function(e){
17181 this.inKeyMode = true;
17185 "down" : function(e){
17186 if(!this.isExpanded()){
17187 this.onTriggerClick();
17189 this.inKeyMode = true;
17194 "enter" : function(e){
17195 // this.onViewClick();
17199 if(this.fireEvent("specialkey", this, e)){
17200 this.onViewClick(false);
17206 "esc" : function(e){
17210 "tab" : function(e){
17213 if(this.fireEvent("specialkey", this, e)){
17214 this.onViewClick(false);
17222 doRelay : function(foo, bar, hname){
17223 if(hname == 'down' || this.scope.isExpanded()){
17224 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17233 this.queryDelay = Math.max(this.queryDelay || 10,
17234 this.mode == 'local' ? 10 : 250);
17237 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17239 if(this.typeAhead){
17240 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17242 if(this.editable !== false){
17243 this.inputEl().on("keyup", this.onKeyUp, this);
17245 if(this.forceSelection){
17246 this.inputEl().on('blur', this.doForce, this);
17250 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17251 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17255 initTickableEvents: function()
17259 if(this.hiddenName){
17261 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17263 this.hiddenField.dom.value =
17264 this.hiddenValue !== undefined ? this.hiddenValue :
17265 this.value !== undefined ? this.value : '';
17267 // prevent input submission
17268 this.el.dom.removeAttribute('name');
17269 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17274 // this.list = this.el.select('ul.dropdown-menu',true).first();
17276 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17277 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17278 if(this.triggerList){
17279 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17282 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17283 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17285 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17286 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17288 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17289 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17291 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17292 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17293 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17296 this.cancelBtn.hide();
17301 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17302 _this.list.setWidth(lw);
17305 this.list.on('mouseover', this.onViewOver, this);
17306 this.list.on('mousemove', this.onViewMove, this);
17308 this.list.on('scroll', this.onViewScroll, this);
17311 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17312 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17315 this.view = new Roo.View(this.list, this.tpl, {
17320 selectedClass: this.selectedClass
17323 //this.view.wrapEl.setDisplayed(false);
17324 this.view.on('click', this.onViewClick, this);
17328 this.store.on('beforeload', this.onBeforeLoad, this);
17329 this.store.on('load', this.onLoad, this);
17330 this.store.on('loadexception', this.onLoadException, this);
17333 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17334 "up" : function(e){
17335 this.inKeyMode = true;
17339 "down" : function(e){
17340 this.inKeyMode = true;
17344 "enter" : function(e){
17345 if(this.fireEvent("specialkey", this, e)){
17346 this.onViewClick(false);
17352 "esc" : function(e){
17353 this.onTickableFooterButtonClick(e, false, false);
17356 "tab" : function(e){
17357 this.fireEvent("specialkey", this, e);
17359 this.onTickableFooterButtonClick(e, false, false);
17366 doRelay : function(e, fn, key){
17367 if(this.scope.isExpanded()){
17368 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17377 this.queryDelay = Math.max(this.queryDelay || 10,
17378 this.mode == 'local' ? 10 : 250);
17381 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17383 if(this.typeAhead){
17384 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17387 if(this.editable !== false){
17388 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17391 this.indicator = this.indicatorEl();
17393 if(this.indicator){
17394 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17395 this.indicator.hide();
17400 onDestroy : function(){
17402 this.view.setStore(null);
17403 this.view.el.removeAllListeners();
17404 this.view.el.remove();
17405 this.view.purgeListeners();
17408 this.list.dom.innerHTML = '';
17412 this.store.un('beforeload', this.onBeforeLoad, this);
17413 this.store.un('load', this.onLoad, this);
17414 this.store.un('loadexception', this.onLoadException, this);
17416 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17420 fireKey : function(e){
17421 if(e.isNavKeyPress() && !this.list.isVisible()){
17422 this.fireEvent("specialkey", this, e);
17427 onResize: function(w, h)
17431 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17433 // if(typeof w != 'number'){
17434 // // we do not handle it!?!?
17437 // var tw = this.trigger.getWidth();
17438 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17439 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17441 // this.inputEl().setWidth( this.adjustWidth('input', x));
17443 // //this.trigger.setStyle('left', x+'px');
17445 // if(this.list && this.listWidth === undefined){
17446 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17447 // this.list.setWidth(lw);
17448 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17456 * Allow or prevent the user from directly editing the field text. If false is passed,
17457 * the user will only be able to select from the items defined in the dropdown list. This method
17458 * is the runtime equivalent of setting the 'editable' config option at config time.
17459 * @param {Boolean} value True to allow the user to directly edit the field text
17461 setEditable : function(value){
17462 if(value == this.editable){
17465 this.editable = value;
17467 this.inputEl().dom.setAttribute('readOnly', true);
17468 this.inputEl().on('mousedown', this.onTriggerClick, this);
17469 this.inputEl().addClass('x-combo-noedit');
17471 this.inputEl().dom.removeAttribute('readOnly');
17472 this.inputEl().un('mousedown', this.onTriggerClick, this);
17473 this.inputEl().removeClass('x-combo-noedit');
17479 onBeforeLoad : function(combo,opts){
17480 if(!this.hasFocus){
17484 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17486 this.restrictHeight();
17487 this.selectedIndex = -1;
17491 onLoad : function(){
17493 this.hasQuery = false;
17495 if(!this.hasFocus){
17499 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17500 this.loading.hide();
17503 if(this.store.getCount() > 0){
17506 this.restrictHeight();
17507 if(this.lastQuery == this.allQuery){
17508 if(this.editable && !this.tickable){
17509 this.inputEl().dom.select();
17513 !this.selectByValue(this.value, true) &&
17516 !this.store.lastOptions ||
17517 typeof(this.store.lastOptions.add) == 'undefined' ||
17518 this.store.lastOptions.add != true
17521 this.select(0, true);
17524 if(this.autoFocus){
17527 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17528 this.taTask.delay(this.typeAheadDelay);
17532 this.onEmptyResults();
17538 onLoadException : function()
17540 this.hasQuery = false;
17542 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17543 this.loading.hide();
17546 if(this.tickable && this.editable){
17551 // only causes errors at present
17552 //Roo.log(this.store.reader.jsonData);
17553 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17555 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17561 onTypeAhead : function(){
17562 if(this.store.getCount() > 0){
17563 var r = this.store.getAt(0);
17564 var newValue = r.data[this.displayField];
17565 var len = newValue.length;
17566 var selStart = this.getRawValue().length;
17568 if(selStart != len){
17569 this.setRawValue(newValue);
17570 this.selectText(selStart, newValue.length);
17576 onSelect : function(record, index){
17578 if(this.fireEvent('beforeselect', this, record, index) !== false){
17580 this.setFromData(index > -1 ? record.data : false);
17583 this.fireEvent('select', this, record, index);
17588 * Returns the currently selected field value or empty string if no value is set.
17589 * @return {String} value The selected value
17591 getValue : function()
17593 if(Roo.isIOS && this.useNativeIOS){
17594 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17598 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17601 if(this.valueField){
17602 return typeof this.value != 'undefined' ? this.value : '';
17604 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17608 getRawValue : function()
17610 if(Roo.isIOS && this.useNativeIOS){
17611 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17614 var v = this.inputEl().getValue();
17620 * Clears any text/value currently set in the field
17622 clearValue : function(){
17624 if(this.hiddenField){
17625 this.hiddenField.dom.value = '';
17628 this.setRawValue('');
17629 this.lastSelectionText = '';
17630 this.lastData = false;
17632 var close = this.closeTriggerEl();
17643 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17644 * will be displayed in the field. If the value does not match the data value of an existing item,
17645 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17646 * Otherwise the field will be blank (although the value will still be set).
17647 * @param {String} value The value to match
17649 setValue : function(v)
17651 if(Roo.isIOS && this.useNativeIOS){
17652 this.setIOSValue(v);
17662 if(this.valueField){
17663 var r = this.findRecord(this.valueField, v);
17665 text = r.data[this.displayField];
17666 }else if(this.valueNotFoundText !== undefined){
17667 text = this.valueNotFoundText;
17670 this.lastSelectionText = text;
17671 if(this.hiddenField){
17672 this.hiddenField.dom.value = v;
17674 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17677 var close = this.closeTriggerEl();
17680 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17686 * @property {Object} the last set data for the element
17691 * Sets the value of the field based on a object which is related to the record format for the store.
17692 * @param {Object} value the value to set as. or false on reset?
17694 setFromData : function(o){
17701 var dv = ''; // display value
17702 var vv = ''; // value value..
17704 if (this.displayField) {
17705 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17707 // this is an error condition!!!
17708 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17711 if(this.valueField){
17712 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17715 var close = this.closeTriggerEl();
17718 if(dv.length || vv * 1 > 0){
17720 this.blockFocus=true;
17726 if(this.hiddenField){
17727 this.hiddenField.dom.value = vv;
17729 this.lastSelectionText = dv;
17730 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17734 // no hidden field.. - we store the value in 'value', but still display
17735 // display field!!!!
17736 this.lastSelectionText = dv;
17737 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17744 reset : function(){
17745 // overridden so that last data is reset..
17752 this.setValue(this.originalValue);
17753 //this.clearInvalid();
17754 this.lastData = false;
17756 this.view.clearSelections();
17762 findRecord : function(prop, value){
17764 if(this.store.getCount() > 0){
17765 this.store.each(function(r){
17766 if(r.data[prop] == value){
17776 getName: function()
17778 // returns hidden if it's set..
17779 if (!this.rendered) {return ''};
17780 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17784 onViewMove : function(e, t){
17785 this.inKeyMode = false;
17789 onViewOver : function(e, t){
17790 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17793 var item = this.view.findItemFromChild(t);
17796 var index = this.view.indexOf(item);
17797 this.select(index, false);
17802 onViewClick : function(view, doFocus, el, e)
17804 var index = this.view.getSelectedIndexes()[0];
17806 var r = this.store.getAt(index);
17810 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17817 Roo.each(this.tickItems, function(v,k){
17819 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17821 _this.tickItems.splice(k, 1);
17823 if(typeof(e) == 'undefined' && view == false){
17824 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17836 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17837 this.tickItems.push(r.data);
17840 if(typeof(e) == 'undefined' && view == false){
17841 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17848 this.onSelect(r, index);
17850 if(doFocus !== false && !this.blockFocus){
17851 this.inputEl().focus();
17856 restrictHeight : function(){
17857 //this.innerList.dom.style.height = '';
17858 //var inner = this.innerList.dom;
17859 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17860 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17861 //this.list.beginUpdate();
17862 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17863 this.list.alignTo(this.inputEl(), this.listAlign);
17864 this.list.alignTo(this.inputEl(), this.listAlign);
17865 //this.list.endUpdate();
17869 onEmptyResults : function(){
17871 if(this.tickable && this.editable){
17872 this.hasFocus = false;
17873 this.restrictHeight();
17881 * Returns true if the dropdown list is expanded, else false.
17883 isExpanded : function(){
17884 return this.list.isVisible();
17888 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17889 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17890 * @param {String} value The data value of the item to select
17891 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17892 * selected item if it is not currently in view (defaults to true)
17893 * @return {Boolean} True if the value matched an item in the list, else false
17895 selectByValue : function(v, scrollIntoView){
17896 if(v !== undefined && v !== null){
17897 var r = this.findRecord(this.valueField || this.displayField, v);
17899 this.select(this.store.indexOf(r), scrollIntoView);
17907 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17908 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17909 * @param {Number} index The zero-based index of the list item to select
17910 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17911 * selected item if it is not currently in view (defaults to true)
17913 select : function(index, scrollIntoView){
17914 this.selectedIndex = index;
17915 this.view.select(index);
17916 if(scrollIntoView !== false){
17917 var el = this.view.getNode(index);
17919 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17922 this.list.scrollChildIntoView(el, false);
17928 selectNext : function(){
17929 var ct = this.store.getCount();
17931 if(this.selectedIndex == -1){
17933 }else if(this.selectedIndex < ct-1){
17934 this.select(this.selectedIndex+1);
17940 selectPrev : function(){
17941 var ct = this.store.getCount();
17943 if(this.selectedIndex == -1){
17945 }else if(this.selectedIndex != 0){
17946 this.select(this.selectedIndex-1);
17952 onKeyUp : function(e){
17953 if(this.editable !== false && !e.isSpecialKey()){
17954 this.lastKey = e.getKey();
17955 this.dqTask.delay(this.queryDelay);
17960 validateBlur : function(){
17961 return !this.list || !this.list.isVisible();
17965 initQuery : function(){
17967 var v = this.getRawValue();
17969 if(this.tickable && this.editable){
17970 v = this.tickableInputEl().getValue();
17977 doForce : function(){
17978 if(this.inputEl().dom.value.length > 0){
17979 this.inputEl().dom.value =
17980 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17986 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17987 * query allowing the query action to be canceled if needed.
17988 * @param {String} query The SQL query to execute
17989 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17990 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17991 * saved in the current store (defaults to false)
17993 doQuery : function(q, forceAll){
17995 if(q === undefined || q === null){
18000 forceAll: forceAll,
18004 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18009 forceAll = qe.forceAll;
18010 if(forceAll === true || (q.length >= this.minChars)){
18012 this.hasQuery = true;
18014 if(this.lastQuery != q || this.alwaysQuery){
18015 this.lastQuery = q;
18016 if(this.mode == 'local'){
18017 this.selectedIndex = -1;
18019 this.store.clearFilter();
18022 if(this.specialFilter){
18023 this.fireEvent('specialfilter', this);
18028 this.store.filter(this.displayField, q);
18031 this.store.fireEvent("datachanged", this.store);
18038 this.store.baseParams[this.queryParam] = q;
18040 var options = {params : this.getParams(q)};
18043 options.add = true;
18044 options.params.start = this.page * this.pageSize;
18047 this.store.load(options);
18050 * this code will make the page width larger, at the beginning, the list not align correctly,
18051 * we should expand the list on onLoad
18052 * so command out it
18057 this.selectedIndex = -1;
18062 this.loadNext = false;
18066 getParams : function(q){
18068 //p[this.queryParam] = q;
18072 p.limit = this.pageSize;
18078 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18080 collapse : function(){
18081 if(!this.isExpanded()){
18087 this.hasFocus = false;
18091 this.cancelBtn.hide();
18092 this.trigger.show();
18095 this.tickableInputEl().dom.value = '';
18096 this.tickableInputEl().blur();
18101 Roo.get(document).un('mousedown', this.collapseIf, this);
18102 Roo.get(document).un('mousewheel', this.collapseIf, this);
18103 if (!this.editable) {
18104 Roo.get(document).un('keydown', this.listKeyPress, this);
18106 this.fireEvent('collapse', this);
18112 collapseIf : function(e){
18113 var in_combo = e.within(this.el);
18114 var in_list = e.within(this.list);
18115 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18117 if (in_combo || in_list || is_list) {
18118 //e.stopPropagation();
18123 this.onTickableFooterButtonClick(e, false, false);
18131 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18133 expand : function(){
18135 if(this.isExpanded() || !this.hasFocus){
18139 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18140 this.list.setWidth(lw);
18146 this.restrictHeight();
18150 this.tickItems = Roo.apply([], this.item);
18153 this.cancelBtn.show();
18154 this.trigger.hide();
18157 this.tickableInputEl().focus();
18162 Roo.get(document).on('mousedown', this.collapseIf, this);
18163 Roo.get(document).on('mousewheel', this.collapseIf, this);
18164 if (!this.editable) {
18165 Roo.get(document).on('keydown', this.listKeyPress, this);
18168 this.fireEvent('expand', this);
18172 // Implements the default empty TriggerField.onTriggerClick function
18173 onTriggerClick : function(e)
18175 Roo.log('trigger click');
18177 if(this.disabled || !this.triggerList){
18182 this.loadNext = false;
18184 if(this.isExpanded()){
18186 if (!this.blockFocus) {
18187 this.inputEl().focus();
18191 this.hasFocus = true;
18192 if(this.triggerAction == 'all') {
18193 this.doQuery(this.allQuery, true);
18195 this.doQuery(this.getRawValue());
18197 if (!this.blockFocus) {
18198 this.inputEl().focus();
18203 onTickableTriggerClick : function(e)
18210 this.loadNext = false;
18211 this.hasFocus = true;
18213 if(this.triggerAction == 'all') {
18214 this.doQuery(this.allQuery, true);
18216 this.doQuery(this.getRawValue());
18220 onSearchFieldClick : function(e)
18222 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18223 this.onTickableFooterButtonClick(e, false, false);
18227 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18232 this.loadNext = false;
18233 this.hasFocus = true;
18235 if(this.triggerAction == 'all') {
18236 this.doQuery(this.allQuery, true);
18238 this.doQuery(this.getRawValue());
18242 listKeyPress : function(e)
18244 //Roo.log('listkeypress');
18245 // scroll to first matching element based on key pres..
18246 if (e.isSpecialKey()) {
18249 var k = String.fromCharCode(e.getKey()).toUpperCase();
18252 var csel = this.view.getSelectedNodes();
18253 var cselitem = false;
18255 var ix = this.view.indexOf(csel[0]);
18256 cselitem = this.store.getAt(ix);
18257 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18263 this.store.each(function(v) {
18265 // start at existing selection.
18266 if (cselitem.id == v.id) {
18272 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18273 match = this.store.indexOf(v);
18279 if (match === false) {
18280 return true; // no more action?
18283 this.view.select(match);
18284 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18285 sn.scrollIntoView(sn.dom.parentNode, false);
18288 onViewScroll : function(e, t){
18290 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
18294 this.hasQuery = true;
18296 this.loading = this.list.select('.loading', true).first();
18298 if(this.loading === null){
18299 this.list.createChild({
18301 cls: 'loading roo-select2-more-results roo-select2-active',
18302 html: 'Loading more results...'
18305 this.loading = this.list.select('.loading', true).first();
18307 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18309 this.loading.hide();
18312 this.loading.show();
18317 this.loadNext = true;
18319 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18324 addItem : function(o)
18326 var dv = ''; // display value
18328 if (this.displayField) {
18329 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18331 // this is an error condition!!!
18332 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18339 var choice = this.choices.createChild({
18341 cls: 'roo-select2-search-choice',
18350 cls: 'roo-select2-search-choice-close fa fa-times',
18355 }, this.searchField);
18357 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18359 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18367 this.inputEl().dom.value = '';
18372 onRemoveItem : function(e, _self, o)
18374 e.preventDefault();
18376 this.lastItem = Roo.apply([], this.item);
18378 var index = this.item.indexOf(o.data) * 1;
18381 Roo.log('not this item?!');
18385 this.item.splice(index, 1);
18390 this.fireEvent('remove', this, e);
18396 syncValue : function()
18398 if(!this.item.length){
18405 Roo.each(this.item, function(i){
18406 if(_this.valueField){
18407 value.push(i[_this.valueField]);
18414 this.value = value.join(',');
18416 if(this.hiddenField){
18417 this.hiddenField.dom.value = this.value;
18420 this.store.fireEvent("datachanged", this.store);
18425 clearItem : function()
18427 if(!this.multiple){
18433 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18441 if(this.tickable && !Roo.isTouch){
18442 this.view.refresh();
18446 inputEl: function ()
18448 if(Roo.isIOS && this.useNativeIOS){
18449 return this.el.select('select.roo-ios-select', true).first();
18452 if(Roo.isTouch && this.mobileTouchView){
18453 return this.el.select('input.form-control',true).first();
18457 return this.searchField;
18460 return this.el.select('input.form-control',true).first();
18463 onTickableFooterButtonClick : function(e, btn, el)
18465 e.preventDefault();
18467 this.lastItem = Roo.apply([], this.item);
18469 if(btn && btn.name == 'cancel'){
18470 this.tickItems = Roo.apply([], this.item);
18479 Roo.each(this.tickItems, function(o){
18487 validate : function()
18489 if(this.getVisibilityEl().hasClass('hidden')){
18493 var v = this.getRawValue();
18496 v = this.getValue();
18499 if(this.disabled || this.allowBlank || v.length){
18504 this.markInvalid();
18508 tickableInputEl : function()
18510 if(!this.tickable || !this.editable){
18511 return this.inputEl();
18514 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18518 getAutoCreateTouchView : function()
18523 cls: 'form-group' //input-group
18529 type : this.inputType,
18530 cls : 'form-control x-combo-noedit',
18531 autocomplete: 'new-password',
18532 placeholder : this.placeholder || '',
18537 input.name = this.name;
18541 input.cls += ' input-' + this.size;
18544 if (this.disabled) {
18545 input.disabled = true;
18549 cls : 'roo-combobox-wrap',
18556 inputblock.cls += ' input-group';
18558 inputblock.cn.unshift({
18560 cls : 'input-group-addon input-group-prepend input-group-text',
18565 if(this.removable && !this.multiple){
18566 inputblock.cls += ' roo-removable';
18568 inputblock.cn.push({
18571 cls : 'roo-combo-removable-btn close'
18575 if(this.hasFeedback && !this.allowBlank){
18577 inputblock.cls += ' has-feedback';
18579 inputblock.cn.push({
18581 cls: 'glyphicon form-control-feedback'
18588 inputblock.cls += (this.before) ? '' : ' input-group';
18590 inputblock.cn.push({
18592 cls : 'input-group-addon input-group-append input-group-text',
18598 var ibwrap = inputblock;
18603 cls: 'roo-select2-choices',
18607 cls: 'roo-select2-search-field',
18620 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18625 cls: 'form-hidden-field'
18631 if(!this.multiple && this.showToggleBtn){
18637 if (this.caret != false) {
18640 cls: 'fa fa-' + this.caret
18647 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18649 Roo.bootstrap.version == 3 ? caret : '',
18652 cls: 'combobox-clear',
18666 combobox.cls += ' roo-select2-container-multi';
18669 var required = this.allowBlank ? {
18671 style: 'display: none'
18674 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18675 tooltip : 'This field is required'
18678 var align = this.labelAlign || this.parentLabelAlign();
18680 if (align ==='left' && this.fieldLabel.length) {
18686 cls : 'control-label col-form-label',
18687 html : this.fieldLabel
18691 cls : 'roo-combobox-wrap ',
18698 var labelCfg = cfg.cn[1];
18699 var contentCfg = cfg.cn[2];
18702 if(this.indicatorpos == 'right'){
18707 cls : 'control-label col-form-label',
18711 html : this.fieldLabel
18717 cls : "roo-combobox-wrap ",
18725 labelCfg = cfg.cn[0];
18726 contentCfg = cfg.cn[1];
18731 if(this.labelWidth > 12){
18732 labelCfg.style = "width: " + this.labelWidth + 'px';
18735 if(this.labelWidth < 13 && this.labelmd == 0){
18736 this.labelmd = this.labelWidth;
18739 if(this.labellg > 0){
18740 labelCfg.cls += ' col-lg-' + this.labellg;
18741 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18744 if(this.labelmd > 0){
18745 labelCfg.cls += ' col-md-' + this.labelmd;
18746 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18749 if(this.labelsm > 0){
18750 labelCfg.cls += ' col-sm-' + this.labelsm;
18751 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18754 if(this.labelxs > 0){
18755 labelCfg.cls += ' col-xs-' + this.labelxs;
18756 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18760 } else if ( this.fieldLabel.length) {
18765 cls : 'control-label',
18766 html : this.fieldLabel
18777 if(this.indicatorpos == 'right'){
18781 cls : 'control-label',
18782 html : this.fieldLabel,
18800 var settings = this;
18802 ['xs','sm','md','lg'].map(function(size){
18803 if (settings[size]) {
18804 cfg.cls += ' col-' + size + '-' + settings[size];
18811 initTouchView : function()
18813 this.renderTouchView();
18815 this.touchViewEl.on('scroll', function(){
18816 this.el.dom.scrollTop = 0;
18819 this.originalValue = this.getValue();
18821 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18823 this.inputEl().on("click", this.showTouchView, this);
18824 if (this.triggerEl) {
18825 this.triggerEl.on("click", this.showTouchView, this);
18829 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18830 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18832 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18834 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18835 this.store.on('load', this.onTouchViewLoad, this);
18836 this.store.on('loadexception', this.onTouchViewLoadException, this);
18838 if(this.hiddenName){
18840 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18842 this.hiddenField.dom.value =
18843 this.hiddenValue !== undefined ? this.hiddenValue :
18844 this.value !== undefined ? this.value : '';
18846 this.el.dom.removeAttribute('name');
18847 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18851 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18852 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18855 if(this.removable && !this.multiple){
18856 var close = this.closeTriggerEl();
18858 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18859 close.on('click', this.removeBtnClick, this, close);
18863 * fix the bug in Safari iOS8
18865 this.inputEl().on("focus", function(e){
18866 document.activeElement.blur();
18869 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18876 renderTouchView : function()
18878 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18879 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18881 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18882 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18885 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18886 this.touchViewBodyEl.setStyle('overflow', 'auto');
18888 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18889 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18892 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18896 showTouchView : function()
18902 this.touchViewHeaderEl.hide();
18904 if(this.modalTitle.length){
18905 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18906 this.touchViewHeaderEl.show();
18909 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18910 this.touchViewEl.show();
18912 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18914 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18915 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18917 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18919 if(this.modalTitle.length){
18920 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18923 this.touchViewBodyEl.setHeight(bodyHeight);
18927 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18929 this.touchViewEl.addClass(['in','show']);
18932 if(this._touchViewMask){
18933 Roo.get(document.body).addClass("x-body-masked");
18934 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18935 this._touchViewMask.setStyle('z-index', 10000);
18936 this._touchViewMask.addClass('show');
18939 this.doTouchViewQuery();
18943 hideTouchView : function()
18945 this.touchViewEl.removeClass(['in','show']);
18949 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18951 this.touchViewEl.setStyle('display', 'none');
18954 if(this._touchViewMask){
18955 this._touchViewMask.removeClass('show');
18956 Roo.get(document.body).removeClass("x-body-masked");
18960 setTouchViewValue : function()
18967 Roo.each(this.tickItems, function(o){
18972 this.hideTouchView();
18975 doTouchViewQuery : function()
18984 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18988 if(!this.alwaysQuery || this.mode == 'local'){
18989 this.onTouchViewLoad();
18996 onTouchViewBeforeLoad : function(combo,opts)
19002 onTouchViewLoad : function()
19004 if(this.store.getCount() < 1){
19005 this.onTouchViewEmptyResults();
19009 this.clearTouchView();
19011 var rawValue = this.getRawValue();
19013 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19015 this.tickItems = [];
19017 this.store.data.each(function(d, rowIndex){
19018 var row = this.touchViewListGroup.createChild(template);
19020 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19021 row.addClass(d.data.cls);
19024 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19027 html : d.data[this.displayField]
19030 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19031 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19034 row.removeClass('selected');
19035 if(!this.multiple && this.valueField &&
19036 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19039 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19040 row.addClass('selected');
19043 if(this.multiple && this.valueField &&
19044 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19048 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19049 this.tickItems.push(d.data);
19052 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19056 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19058 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19060 if(this.modalTitle.length){
19061 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19064 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19066 if(this.mobile_restrict_height && listHeight < bodyHeight){
19067 this.touchViewBodyEl.setHeight(listHeight);
19072 if(firstChecked && listHeight > bodyHeight){
19073 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19078 onTouchViewLoadException : function()
19080 this.hideTouchView();
19083 onTouchViewEmptyResults : function()
19085 this.clearTouchView();
19087 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19089 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19093 clearTouchView : function()
19095 this.touchViewListGroup.dom.innerHTML = '';
19098 onTouchViewClick : function(e, el, o)
19100 e.preventDefault();
19103 var rowIndex = o.rowIndex;
19105 var r = this.store.getAt(rowIndex);
19107 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19109 if(!this.multiple){
19110 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19111 c.dom.removeAttribute('checked');
19114 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19116 this.setFromData(r.data);
19118 var close = this.closeTriggerEl();
19124 this.hideTouchView();
19126 this.fireEvent('select', this, r, rowIndex);
19131 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19132 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19133 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19137 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19138 this.addItem(r.data);
19139 this.tickItems.push(r.data);
19143 getAutoCreateNativeIOS : function()
19146 cls: 'form-group' //input-group,
19151 cls : 'roo-ios-select'
19155 combobox.name = this.name;
19158 if (this.disabled) {
19159 combobox.disabled = true;
19162 var settings = this;
19164 ['xs','sm','md','lg'].map(function(size){
19165 if (settings[size]) {
19166 cfg.cls += ' col-' + size + '-' + settings[size];
19176 initIOSView : function()
19178 this.store.on('load', this.onIOSViewLoad, this);
19183 onIOSViewLoad : function()
19185 if(this.store.getCount() < 1){
19189 this.clearIOSView();
19191 if(this.allowBlank) {
19193 var default_text = '-- SELECT --';
19195 if(this.placeholder.length){
19196 default_text = this.placeholder;
19199 if(this.emptyTitle.length){
19200 default_text += ' - ' + this.emptyTitle + ' -';
19203 var opt = this.inputEl().createChild({
19206 html : default_text
19210 o[this.valueField] = 0;
19211 o[this.displayField] = default_text;
19213 this.ios_options.push({
19220 this.store.data.each(function(d, rowIndex){
19224 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19225 html = d.data[this.displayField];
19230 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19231 value = d.data[this.valueField];
19240 if(this.value == d.data[this.valueField]){
19241 option['selected'] = true;
19244 var opt = this.inputEl().createChild(option);
19246 this.ios_options.push({
19253 this.inputEl().on('change', function(){
19254 this.fireEvent('select', this);
19259 clearIOSView: function()
19261 this.inputEl().dom.innerHTML = '';
19263 this.ios_options = [];
19266 setIOSValue: function(v)
19270 if(!this.ios_options){
19274 Roo.each(this.ios_options, function(opts){
19276 opts.el.dom.removeAttribute('selected');
19278 if(opts.data[this.valueField] != v){
19282 opts.el.dom.setAttribute('selected', true);
19288 * @cfg {Boolean} grow
19292 * @cfg {Number} growMin
19296 * @cfg {Number} growMax
19305 Roo.apply(Roo.bootstrap.ComboBox, {
19309 cls: 'modal-header',
19331 cls: 'list-group-item',
19335 cls: 'roo-combobox-list-group-item-value'
19339 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19353 listItemCheckbox : {
19355 cls: 'list-group-item',
19359 cls: 'roo-combobox-list-group-item-value'
19363 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19379 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19384 cls: 'modal-footer',
19392 cls: 'col-xs-6 text-left',
19395 cls: 'btn btn-danger roo-touch-view-cancel',
19401 cls: 'col-xs-6 text-right',
19404 cls: 'btn btn-success roo-touch-view-ok',
19415 Roo.apply(Roo.bootstrap.ComboBox, {
19417 touchViewTemplate : {
19419 cls: 'modal fade roo-combobox-touch-view',
19423 cls: 'modal-dialog',
19424 style : 'position:fixed', // we have to fix position....
19428 cls: 'modal-content',
19430 Roo.bootstrap.ComboBox.header,
19431 Roo.bootstrap.ComboBox.body,
19432 Roo.bootstrap.ComboBox.footer
19441 * Ext JS Library 1.1.1
19442 * Copyright(c) 2006-2007, Ext JS, LLC.
19444 * Originally Released Under LGPL - original licence link has changed is not relivant.
19447 * <script type="text/javascript">
19452 * @extends Roo.util.Observable
19453 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19454 * This class also supports single and multi selection modes. <br>
19455 * Create a data model bound view:
19457 var store = new Roo.data.Store(...);
19459 var view = new Roo.View({
19461 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19463 singleSelect: true,
19464 selectedClass: "ydataview-selected",
19468 // listen for node click?
19469 view.on("click", function(vw, index, node, e){
19470 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19474 dataModel.load("foobar.xml");
19476 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19478 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19479 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19481 * Note: old style constructor is still suported (container, template, config)
19484 * Create a new View
19485 * @param {Object} config The config object
19488 Roo.View = function(config, depreciated_tpl, depreciated_config){
19490 this.parent = false;
19492 if (typeof(depreciated_tpl) == 'undefined') {
19493 // new way.. - universal constructor.
19494 Roo.apply(this, config);
19495 this.el = Roo.get(this.el);
19498 this.el = Roo.get(config);
19499 this.tpl = depreciated_tpl;
19500 Roo.apply(this, depreciated_config);
19502 this.wrapEl = this.el.wrap().wrap();
19503 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19506 if(typeof(this.tpl) == "string"){
19507 this.tpl = new Roo.Template(this.tpl);
19509 // support xtype ctors..
19510 this.tpl = new Roo.factory(this.tpl, Roo);
19514 this.tpl.compile();
19519 * @event beforeclick
19520 * Fires before a click is processed. Returns false to cancel the default action.
19521 * @param {Roo.View} this
19522 * @param {Number} index The index of the target node
19523 * @param {HTMLElement} node The target node
19524 * @param {Roo.EventObject} e The raw event object
19526 "beforeclick" : true,
19529 * Fires when a template node is clicked.
19530 * @param {Roo.View} this
19531 * @param {Number} index The index of the target node
19532 * @param {HTMLElement} node The target node
19533 * @param {Roo.EventObject} e The raw event object
19538 * Fires when a template node is double clicked.
19539 * @param {Roo.View} this
19540 * @param {Number} index The index of the target node
19541 * @param {HTMLElement} node The target node
19542 * @param {Roo.EventObject} e The raw event object
19546 * @event contextmenu
19547 * Fires when a template node is right clicked.
19548 * @param {Roo.View} this
19549 * @param {Number} index The index of the target node
19550 * @param {HTMLElement} node The target node
19551 * @param {Roo.EventObject} e The raw event object
19553 "contextmenu" : true,
19555 * @event selectionchange
19556 * Fires when the selected nodes change.
19557 * @param {Roo.View} this
19558 * @param {Array} selections Array of the selected nodes
19560 "selectionchange" : true,
19563 * @event beforeselect
19564 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19565 * @param {Roo.View} this
19566 * @param {HTMLElement} node The node to be selected
19567 * @param {Array} selections Array of currently selected nodes
19569 "beforeselect" : true,
19571 * @event preparedata
19572 * Fires on every row to render, to allow you to change the data.
19573 * @param {Roo.View} this
19574 * @param {Object} data to be rendered (change this)
19576 "preparedata" : true
19584 "click": this.onClick,
19585 "dblclick": this.onDblClick,
19586 "contextmenu": this.onContextMenu,
19590 this.selections = [];
19592 this.cmp = new Roo.CompositeElementLite([]);
19594 this.store = Roo.factory(this.store, Roo.data);
19595 this.setStore(this.store, true);
19598 if ( this.footer && this.footer.xtype) {
19600 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19602 this.footer.dataSource = this.store;
19603 this.footer.container = fctr;
19604 this.footer = Roo.factory(this.footer, Roo);
19605 fctr.insertFirst(this.el);
19607 // this is a bit insane - as the paging toolbar seems to detach the el..
19608 // dom.parentNode.parentNode.parentNode
19609 // they get detached?
19613 Roo.View.superclass.constructor.call(this);
19618 Roo.extend(Roo.View, Roo.util.Observable, {
19621 * @cfg {Roo.data.Store} store Data store to load data from.
19626 * @cfg {String|Roo.Element} el The container element.
19631 * @cfg {String|Roo.Template} tpl The template used by this View
19635 * @cfg {String} dataName the named area of the template to use as the data area
19636 * Works with domtemplates roo-name="name"
19640 * @cfg {String} selectedClass The css class to add to selected nodes
19642 selectedClass : "x-view-selected",
19644 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19649 * @cfg {String} text to display on mask (default Loading)
19653 * @cfg {Boolean} multiSelect Allow multiple selection
19655 multiSelect : false,
19657 * @cfg {Boolean} singleSelect Allow single selection
19659 singleSelect: false,
19662 * @cfg {Boolean} toggleSelect - selecting
19664 toggleSelect : false,
19667 * @cfg {Boolean} tickable - selecting
19672 * Returns the element this view is bound to.
19673 * @return {Roo.Element}
19675 getEl : function(){
19676 return this.wrapEl;
19682 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19684 refresh : function(){
19685 //Roo.log('refresh');
19688 // if we are using something like 'domtemplate', then
19689 // the what gets used is:
19690 // t.applySubtemplate(NAME, data, wrapping data..)
19691 // the outer template then get' applied with
19692 // the store 'extra data'
19693 // and the body get's added to the
19694 // roo-name="data" node?
19695 // <span class='roo-tpl-{name}'></span> ?????
19699 this.clearSelections();
19700 this.el.update("");
19702 var records = this.store.getRange();
19703 if(records.length < 1) {
19705 // is this valid?? = should it render a template??
19707 this.el.update(this.emptyText);
19711 if (this.dataName) {
19712 this.el.update(t.apply(this.store.meta)); //????
19713 el = this.el.child('.roo-tpl-' + this.dataName);
19716 for(var i = 0, len = records.length; i < len; i++){
19717 var data = this.prepareData(records[i].data, i, records[i]);
19718 this.fireEvent("preparedata", this, data, i, records[i]);
19720 var d = Roo.apply({}, data);
19723 Roo.apply(d, {'roo-id' : Roo.id()});
19727 Roo.each(this.parent.item, function(item){
19728 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19731 Roo.apply(d, {'roo-data-checked' : 'checked'});
19735 html[html.length] = Roo.util.Format.trim(
19737 t.applySubtemplate(this.dataName, d, this.store.meta) :
19744 el.update(html.join(""));
19745 this.nodes = el.dom.childNodes;
19746 this.updateIndexes(0);
19751 * Function to override to reformat the data that is sent to
19752 * the template for each node.
19753 * DEPRICATED - use the preparedata event handler.
19754 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19755 * a JSON object for an UpdateManager bound view).
19757 prepareData : function(data, index, record)
19759 this.fireEvent("preparedata", this, data, index, record);
19763 onUpdate : function(ds, record){
19764 // Roo.log('on update');
19765 this.clearSelections();
19766 var index = this.store.indexOf(record);
19767 var n = this.nodes[index];
19768 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19769 n.parentNode.removeChild(n);
19770 this.updateIndexes(index, index);
19776 onAdd : function(ds, records, index)
19778 //Roo.log(['on Add', ds, records, index] );
19779 this.clearSelections();
19780 if(this.nodes.length == 0){
19784 var n = this.nodes[index];
19785 for(var i = 0, len = records.length; i < len; i++){
19786 var d = this.prepareData(records[i].data, i, records[i]);
19788 this.tpl.insertBefore(n, d);
19791 this.tpl.append(this.el, d);
19794 this.updateIndexes(index);
19797 onRemove : function(ds, record, index){
19798 // Roo.log('onRemove');
19799 this.clearSelections();
19800 var el = this.dataName ?
19801 this.el.child('.roo-tpl-' + this.dataName) :
19804 el.dom.removeChild(this.nodes[index]);
19805 this.updateIndexes(index);
19809 * Refresh an individual node.
19810 * @param {Number} index
19812 refreshNode : function(index){
19813 this.onUpdate(this.store, this.store.getAt(index));
19816 updateIndexes : function(startIndex, endIndex){
19817 var ns = this.nodes;
19818 startIndex = startIndex || 0;
19819 endIndex = endIndex || ns.length - 1;
19820 for(var i = startIndex; i <= endIndex; i++){
19821 ns[i].nodeIndex = i;
19826 * Changes the data store this view uses and refresh the view.
19827 * @param {Store} store
19829 setStore : function(store, initial){
19830 if(!initial && this.store){
19831 this.store.un("datachanged", this.refresh);
19832 this.store.un("add", this.onAdd);
19833 this.store.un("remove", this.onRemove);
19834 this.store.un("update", this.onUpdate);
19835 this.store.un("clear", this.refresh);
19836 this.store.un("beforeload", this.onBeforeLoad);
19837 this.store.un("load", this.onLoad);
19838 this.store.un("loadexception", this.onLoad);
19842 store.on("datachanged", this.refresh, this);
19843 store.on("add", this.onAdd, this);
19844 store.on("remove", this.onRemove, this);
19845 store.on("update", this.onUpdate, this);
19846 store.on("clear", this.refresh, this);
19847 store.on("beforeload", this.onBeforeLoad, this);
19848 store.on("load", this.onLoad, this);
19849 store.on("loadexception", this.onLoad, this);
19857 * onbeforeLoad - masks the loading area.
19860 onBeforeLoad : function(store,opts)
19862 //Roo.log('onBeforeLoad');
19864 this.el.update("");
19866 this.el.mask(this.mask ? this.mask : "Loading" );
19868 onLoad : function ()
19875 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19876 * @param {HTMLElement} node
19877 * @return {HTMLElement} The template node
19879 findItemFromChild : function(node){
19880 var el = this.dataName ?
19881 this.el.child('.roo-tpl-' + this.dataName,true) :
19884 if(!node || node.parentNode == el){
19887 var p = node.parentNode;
19888 while(p && p != el){
19889 if(p.parentNode == el){
19898 onClick : function(e){
19899 var item = this.findItemFromChild(e.getTarget());
19901 var index = this.indexOf(item);
19902 if(this.onItemClick(item, index, e) !== false){
19903 this.fireEvent("click", this, index, item, e);
19906 this.clearSelections();
19911 onContextMenu : function(e){
19912 var item = this.findItemFromChild(e.getTarget());
19914 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19919 onDblClick : function(e){
19920 var item = this.findItemFromChild(e.getTarget());
19922 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19926 onItemClick : function(item, index, e)
19928 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19931 if (this.toggleSelect) {
19932 var m = this.isSelected(item) ? 'unselect' : 'select';
19935 _t[m](item, true, false);
19938 if(this.multiSelect || this.singleSelect){
19939 if(this.multiSelect && e.shiftKey && this.lastSelection){
19940 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19942 this.select(item, this.multiSelect && e.ctrlKey);
19943 this.lastSelection = item;
19946 if(!this.tickable){
19947 e.preventDefault();
19955 * Get the number of selected nodes.
19958 getSelectionCount : function(){
19959 return this.selections.length;
19963 * Get the currently selected nodes.
19964 * @return {Array} An array of HTMLElements
19966 getSelectedNodes : function(){
19967 return this.selections;
19971 * Get the indexes of the selected nodes.
19974 getSelectedIndexes : function(){
19975 var indexes = [], s = this.selections;
19976 for(var i = 0, len = s.length; i < len; i++){
19977 indexes.push(s[i].nodeIndex);
19983 * Clear all selections
19984 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19986 clearSelections : function(suppressEvent){
19987 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19988 this.cmp.elements = this.selections;
19989 this.cmp.removeClass(this.selectedClass);
19990 this.selections = [];
19991 if(!suppressEvent){
19992 this.fireEvent("selectionchange", this, this.selections);
19998 * Returns true if the passed node is selected
19999 * @param {HTMLElement/Number} node The node or node index
20000 * @return {Boolean}
20002 isSelected : function(node){
20003 var s = this.selections;
20007 node = this.getNode(node);
20008 return s.indexOf(node) !== -1;
20013 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20014 * @param {Boolean} keepExisting (optional) true to keep existing selections
20015 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20017 select : function(nodeInfo, keepExisting, suppressEvent){
20018 if(nodeInfo instanceof Array){
20020 this.clearSelections(true);
20022 for(var i = 0, len = nodeInfo.length; i < len; i++){
20023 this.select(nodeInfo[i], true, true);
20027 var node = this.getNode(nodeInfo);
20028 if(!node || this.isSelected(node)){
20029 return; // already selected.
20032 this.clearSelections(true);
20035 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20036 Roo.fly(node).addClass(this.selectedClass);
20037 this.selections.push(node);
20038 if(!suppressEvent){
20039 this.fireEvent("selectionchange", this, this.selections);
20047 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20048 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20049 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20051 unselect : function(nodeInfo, keepExisting, suppressEvent)
20053 if(nodeInfo instanceof Array){
20054 Roo.each(this.selections, function(s) {
20055 this.unselect(s, nodeInfo);
20059 var node = this.getNode(nodeInfo);
20060 if(!node || !this.isSelected(node)){
20061 //Roo.log("not selected");
20062 return; // not selected.
20066 Roo.each(this.selections, function(s) {
20068 Roo.fly(node).removeClass(this.selectedClass);
20075 this.selections= ns;
20076 this.fireEvent("selectionchange", this, this.selections);
20080 * Gets a template node.
20081 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20082 * @return {HTMLElement} The node or null if it wasn't found
20084 getNode : function(nodeInfo){
20085 if(typeof nodeInfo == "string"){
20086 return document.getElementById(nodeInfo);
20087 }else if(typeof nodeInfo == "number"){
20088 return this.nodes[nodeInfo];
20094 * Gets a range template nodes.
20095 * @param {Number} startIndex
20096 * @param {Number} endIndex
20097 * @return {Array} An array of nodes
20099 getNodes : function(start, end){
20100 var ns = this.nodes;
20101 start = start || 0;
20102 end = typeof end == "undefined" ? ns.length - 1 : end;
20105 for(var i = start; i <= end; i++){
20109 for(var i = start; i >= end; i--){
20117 * Finds the index of the passed node
20118 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20119 * @return {Number} The index of the node or -1
20121 indexOf : function(node){
20122 node = this.getNode(node);
20123 if(typeof node.nodeIndex == "number"){
20124 return node.nodeIndex;
20126 var ns = this.nodes;
20127 for(var i = 0, len = ns.length; i < len; i++){
20138 * based on jquery fullcalendar
20142 Roo.bootstrap = Roo.bootstrap || {};
20144 * @class Roo.bootstrap.Calendar
20145 * @extends Roo.bootstrap.Component
20146 * Bootstrap Calendar class
20147 * @cfg {Boolean} loadMask (true|false) default false
20148 * @cfg {Object} header generate the user specific header of the calendar, default false
20151 * Create a new Container
20152 * @param {Object} config The config object
20157 Roo.bootstrap.Calendar = function(config){
20158 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20162 * Fires when a date is selected
20163 * @param {DatePicker} this
20164 * @param {Date} date The selected date
20168 * @event monthchange
20169 * Fires when the displayed month changes
20170 * @param {DatePicker} this
20171 * @param {Date} date The selected month
20173 'monthchange': true,
20175 * @event evententer
20176 * Fires when mouse over an event
20177 * @param {Calendar} this
20178 * @param {event} Event
20180 'evententer': true,
20182 * @event eventleave
20183 * Fires when the mouse leaves an
20184 * @param {Calendar} this
20187 'eventleave': true,
20189 * @event eventclick
20190 * Fires when the mouse click an
20191 * @param {Calendar} this
20200 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20203 * @cfg {Number} startDay
20204 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20212 getAutoCreate : function(){
20215 var fc_button = function(name, corner, style, content ) {
20216 return Roo.apply({},{
20218 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20220 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20223 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20234 style : 'width:100%',
20241 cls : 'fc-header-left',
20243 fc_button('prev', 'left', 'arrow', '‹' ),
20244 fc_button('next', 'right', 'arrow', '›' ),
20245 { tag: 'span', cls: 'fc-header-space' },
20246 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20254 cls : 'fc-header-center',
20258 cls: 'fc-header-title',
20261 html : 'month / year'
20269 cls : 'fc-header-right',
20271 /* fc_button('month', 'left', '', 'month' ),
20272 fc_button('week', '', '', 'week' ),
20273 fc_button('day', 'right', '', 'day' )
20285 header = this.header;
20288 var cal_heads = function() {
20290 // fixme - handle this.
20292 for (var i =0; i < Date.dayNames.length; i++) {
20293 var d = Date.dayNames[i];
20296 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20297 html : d.substring(0,3)
20301 ret[0].cls += ' fc-first';
20302 ret[6].cls += ' fc-last';
20305 var cal_cell = function(n) {
20308 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20313 cls: 'fc-day-number',
20317 cls: 'fc-day-content',
20321 style: 'position: relative;' // height: 17px;
20333 var cal_rows = function() {
20336 for (var r = 0; r < 6; r++) {
20343 for (var i =0; i < Date.dayNames.length; i++) {
20344 var d = Date.dayNames[i];
20345 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20348 row.cn[0].cls+=' fc-first';
20349 row.cn[0].cn[0].style = 'min-height:90px';
20350 row.cn[6].cls+=' fc-last';
20354 ret[0].cls += ' fc-first';
20355 ret[4].cls += ' fc-prev-last';
20356 ret[5].cls += ' fc-last';
20363 cls: 'fc-border-separate',
20364 style : 'width:100%',
20372 cls : 'fc-first fc-last',
20390 cls : 'fc-content',
20391 style : "position: relative;",
20394 cls : 'fc-view fc-view-month fc-grid',
20395 style : 'position: relative',
20396 unselectable : 'on',
20399 cls : 'fc-event-container',
20400 style : 'position:absolute;z-index:8;top:0;left:0;'
20418 initEvents : function()
20421 throw "can not find store for calendar";
20427 style: "text-align:center",
20431 style: "background-color:white;width:50%;margin:250 auto",
20435 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20446 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20448 var size = this.el.select('.fc-content', true).first().getSize();
20449 this.maskEl.setSize(size.width, size.height);
20450 this.maskEl.enableDisplayMode("block");
20451 if(!this.loadMask){
20452 this.maskEl.hide();
20455 this.store = Roo.factory(this.store, Roo.data);
20456 this.store.on('load', this.onLoad, this);
20457 this.store.on('beforeload', this.onBeforeLoad, this);
20461 this.cells = this.el.select('.fc-day',true);
20462 //Roo.log(this.cells);
20463 this.textNodes = this.el.query('.fc-day-number');
20464 this.cells.addClassOnOver('fc-state-hover');
20466 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20467 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20468 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20469 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20471 this.on('monthchange', this.onMonthChange, this);
20473 this.update(new Date().clearTime());
20476 resize : function() {
20477 var sz = this.el.getSize();
20479 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20480 this.el.select('.fc-day-content div',true).setHeight(34);
20485 showPrevMonth : function(e){
20486 this.update(this.activeDate.add("mo", -1));
20488 showToday : function(e){
20489 this.update(new Date().clearTime());
20492 showNextMonth : function(e){
20493 this.update(this.activeDate.add("mo", 1));
20497 showPrevYear : function(){
20498 this.update(this.activeDate.add("y", -1));
20502 showNextYear : function(){
20503 this.update(this.activeDate.add("y", 1));
20508 update : function(date)
20510 var vd = this.activeDate;
20511 this.activeDate = date;
20512 // if(vd && this.el){
20513 // var t = date.getTime();
20514 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20515 // Roo.log('using add remove');
20517 // this.fireEvent('monthchange', this, date);
20519 // this.cells.removeClass("fc-state-highlight");
20520 // this.cells.each(function(c){
20521 // if(c.dateValue == t){
20522 // c.addClass("fc-state-highlight");
20523 // setTimeout(function(){
20524 // try{c.dom.firstChild.focus();}catch(e){}
20534 var days = date.getDaysInMonth();
20536 var firstOfMonth = date.getFirstDateOfMonth();
20537 var startingPos = firstOfMonth.getDay()-this.startDay;
20539 if(startingPos < this.startDay){
20543 var pm = date.add(Date.MONTH, -1);
20544 var prevStart = pm.getDaysInMonth()-startingPos;
20546 this.cells = this.el.select('.fc-day',true);
20547 this.textNodes = this.el.query('.fc-day-number');
20548 this.cells.addClassOnOver('fc-state-hover');
20550 var cells = this.cells.elements;
20551 var textEls = this.textNodes;
20553 Roo.each(cells, function(cell){
20554 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20557 days += startingPos;
20559 // convert everything to numbers so it's fast
20560 var day = 86400000;
20561 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20564 //Roo.log(prevStart);
20566 var today = new Date().clearTime().getTime();
20567 var sel = date.clearTime().getTime();
20568 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20569 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20570 var ddMatch = this.disabledDatesRE;
20571 var ddText = this.disabledDatesText;
20572 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20573 var ddaysText = this.disabledDaysText;
20574 var format = this.format;
20576 var setCellClass = function(cal, cell){
20580 //Roo.log('set Cell Class');
20582 var t = d.getTime();
20586 cell.dateValue = t;
20588 cell.className += " fc-today";
20589 cell.className += " fc-state-highlight";
20590 cell.title = cal.todayText;
20593 // disable highlight in other month..
20594 //cell.className += " fc-state-highlight";
20599 cell.className = " fc-state-disabled";
20600 cell.title = cal.minText;
20604 cell.className = " fc-state-disabled";
20605 cell.title = cal.maxText;
20609 if(ddays.indexOf(d.getDay()) != -1){
20610 cell.title = ddaysText;
20611 cell.className = " fc-state-disabled";
20614 if(ddMatch && format){
20615 var fvalue = d.dateFormat(format);
20616 if(ddMatch.test(fvalue)){
20617 cell.title = ddText.replace("%0", fvalue);
20618 cell.className = " fc-state-disabled";
20622 if (!cell.initialClassName) {
20623 cell.initialClassName = cell.dom.className;
20626 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20631 for(; i < startingPos; i++) {
20632 textEls[i].innerHTML = (++prevStart);
20633 d.setDate(d.getDate()+1);
20635 cells[i].className = "fc-past fc-other-month";
20636 setCellClass(this, cells[i]);
20641 for(; i < days; i++){
20642 intDay = i - startingPos + 1;
20643 textEls[i].innerHTML = (intDay);
20644 d.setDate(d.getDate()+1);
20646 cells[i].className = ''; // "x-date-active";
20647 setCellClass(this, cells[i]);
20651 for(; i < 42; i++) {
20652 textEls[i].innerHTML = (++extraDays);
20653 d.setDate(d.getDate()+1);
20655 cells[i].className = "fc-future fc-other-month";
20656 setCellClass(this, cells[i]);
20659 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20661 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20663 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20664 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20666 if(totalRows != 6){
20667 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20668 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20671 this.fireEvent('monthchange', this, date);
20675 if(!this.internalRender){
20676 var main = this.el.dom.firstChild;
20677 var w = main.offsetWidth;
20678 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20679 Roo.fly(main).setWidth(w);
20680 this.internalRender = true;
20681 // opera does not respect the auto grow header center column
20682 // then, after it gets a width opera refuses to recalculate
20683 // without a second pass
20684 if(Roo.isOpera && !this.secondPass){
20685 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20686 this.secondPass = true;
20687 this.update.defer(10, this, [date]);
20694 findCell : function(dt) {
20695 dt = dt.clearTime().getTime();
20697 this.cells.each(function(c){
20698 //Roo.log("check " +c.dateValue + '?=' + dt);
20699 if(c.dateValue == dt){
20709 findCells : function(ev) {
20710 var s = ev.start.clone().clearTime().getTime();
20712 var e= ev.end.clone().clearTime().getTime();
20715 this.cells.each(function(c){
20716 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20718 if(c.dateValue > e){
20721 if(c.dateValue < s){
20730 // findBestRow: function(cells)
20734 // for (var i =0 ; i < cells.length;i++) {
20735 // ret = Math.max(cells[i].rows || 0,ret);
20742 addItem : function(ev)
20744 // look for vertical location slot in
20745 var cells = this.findCells(ev);
20747 // ev.row = this.findBestRow(cells);
20749 // work out the location.
20753 for(var i =0; i < cells.length; i++) {
20755 cells[i].row = cells[0].row;
20758 cells[i].row = cells[i].row + 1;
20768 if (crow.start.getY() == cells[i].getY()) {
20770 crow.end = cells[i];
20787 cells[0].events.push(ev);
20789 this.calevents.push(ev);
20792 clearEvents: function() {
20794 if(!this.calevents){
20798 Roo.each(this.cells.elements, function(c){
20804 Roo.each(this.calevents, function(e) {
20805 Roo.each(e.els, function(el) {
20806 el.un('mouseenter' ,this.onEventEnter, this);
20807 el.un('mouseleave' ,this.onEventLeave, this);
20812 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20818 renderEvents: function()
20822 this.cells.each(function(c) {
20831 if(c.row != c.events.length){
20832 r = 4 - (4 - (c.row - c.events.length));
20835 c.events = ev.slice(0, r);
20836 c.more = ev.slice(r);
20838 if(c.more.length && c.more.length == 1){
20839 c.events.push(c.more.pop());
20842 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20846 this.cells.each(function(c) {
20848 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20851 for (var e = 0; e < c.events.length; e++){
20852 var ev = c.events[e];
20853 var rows = ev.rows;
20855 for(var i = 0; i < rows.length; i++) {
20857 // how many rows should it span..
20860 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20861 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20863 unselectable : "on",
20866 cls: 'fc-event-inner',
20870 // cls: 'fc-event-time',
20871 // html : cells.length > 1 ? '' : ev.time
20875 cls: 'fc-event-title',
20876 html : String.format('{0}', ev.title)
20883 cls: 'ui-resizable-handle ui-resizable-e',
20884 html : '  '
20891 cfg.cls += ' fc-event-start';
20893 if ((i+1) == rows.length) {
20894 cfg.cls += ' fc-event-end';
20897 var ctr = _this.el.select('.fc-event-container',true).first();
20898 var cg = ctr.createChild(cfg);
20900 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20901 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20903 var r = (c.more.length) ? 1 : 0;
20904 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20905 cg.setWidth(ebox.right - sbox.x -2);
20907 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20908 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20909 cg.on('click', _this.onEventClick, _this, ev);
20920 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20921 style : 'position: absolute',
20922 unselectable : "on",
20925 cls: 'fc-event-inner',
20929 cls: 'fc-event-title',
20937 cls: 'ui-resizable-handle ui-resizable-e',
20938 html : '  '
20944 var ctr = _this.el.select('.fc-event-container',true).first();
20945 var cg = ctr.createChild(cfg);
20947 var sbox = c.select('.fc-day-content',true).first().getBox();
20948 var ebox = c.select('.fc-day-content',true).first().getBox();
20950 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20951 cg.setWidth(ebox.right - sbox.x -2);
20953 cg.on('click', _this.onMoreEventClick, _this, c.more);
20963 onEventEnter: function (e, el,event,d) {
20964 this.fireEvent('evententer', this, el, event);
20967 onEventLeave: function (e, el,event,d) {
20968 this.fireEvent('eventleave', this, el, event);
20971 onEventClick: function (e, el,event,d) {
20972 this.fireEvent('eventclick', this, el, event);
20975 onMonthChange: function () {
20979 onMoreEventClick: function(e, el, more)
20983 this.calpopover.placement = 'right';
20984 this.calpopover.setTitle('More');
20986 this.calpopover.setContent('');
20988 var ctr = this.calpopover.el.select('.popover-content', true).first();
20990 Roo.each(more, function(m){
20992 cls : 'fc-event-hori fc-event-draggable',
20995 var cg = ctr.createChild(cfg);
20997 cg.on('click', _this.onEventClick, _this, m);
21000 this.calpopover.show(el);
21005 onLoad: function ()
21007 this.calevents = [];
21010 if(this.store.getCount() > 0){
21011 this.store.data.each(function(d){
21014 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21015 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21016 time : d.data.start_time,
21017 title : d.data.title,
21018 description : d.data.description,
21019 venue : d.data.venue
21024 this.renderEvents();
21026 if(this.calevents.length && this.loadMask){
21027 this.maskEl.hide();
21031 onBeforeLoad: function()
21033 this.clearEvents();
21035 this.maskEl.show();
21049 * @class Roo.bootstrap.Popover
21050 * @extends Roo.bootstrap.Component
21051 * Bootstrap Popover class
21052 * @cfg {String} html contents of the popover (or false to use children..)
21053 * @cfg {String} title of popover (or false to hide)
21054 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21055 * @cfg {String} trigger click || hover (or false to trigger manually)
21056 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21057 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21058 * - if false and it has a 'parent' then it will be automatically added to that element
21059 * - if string - Roo.get will be called
21060 * @cfg {Number} delay - delay before showing
21063 * Create a new Popover
21064 * @param {Object} config The config object
21067 Roo.bootstrap.Popover = function(config){
21068 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21074 * After the popover show
21076 * @param {Roo.bootstrap.Popover} this
21081 * After the popover hide
21083 * @param {Roo.bootstrap.Popover} this
21089 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21094 placement : 'right',
21095 trigger : 'hover', // hover
21101 can_build_overlaid : false,
21103 maskEl : false, // the mask element
21106 alignEl : false, // when show is called with an element - this get's stored.
21108 getChildContainer : function()
21110 return this.contentEl;
21113 getPopoverHeader : function()
21115 this.title = true; // flag not to hide it..
21116 this.headerEl.addClass('p-0');
21117 return this.headerEl
21121 getAutoCreate : function(){
21124 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21125 style: 'display:block',
21131 cls : 'popover-inner ',
21135 cls: 'popover-title popover-header',
21136 html : this.title === false ? '' : this.title
21139 cls : 'popover-content popover-body ' + (this.cls || ''),
21140 html : this.html || ''
21151 * @param {string} the title
21153 setTitle: function(str)
21157 this.headerEl.dom.innerHTML = str;
21162 * @param {string} the body content
21164 setContent: function(str)
21167 if (this.contentEl) {
21168 this.contentEl.dom.innerHTML = str;
21172 // as it get's added to the bottom of the page.
21173 onRender : function(ct, position)
21175 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21180 var cfg = Roo.apply({}, this.getAutoCreate());
21184 cfg.cls += ' ' + this.cls;
21187 cfg.style = this.style;
21189 //Roo.log("adding to ");
21190 this.el = Roo.get(document.body).createChild(cfg, position);
21191 // Roo.log(this.el);
21194 this.contentEl = this.el.select('.popover-content',true).first();
21195 this.headerEl = this.el.select('.popover-title',true).first();
21198 if(typeof(this.items) != 'undefined'){
21199 var items = this.items;
21202 for(var i =0;i < items.length;i++) {
21203 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21207 this.items = nitems;
21209 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21210 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21217 resizeMask : function()
21219 this.maskEl.setSize(
21220 Roo.lib.Dom.getViewWidth(true),
21221 Roo.lib.Dom.getViewHeight(true)
21225 initEvents : function()
21229 Roo.bootstrap.Popover.register(this);
21232 this.arrowEl = this.el.select('.arrow',true).first();
21233 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21234 this.el.enableDisplayMode('block');
21238 if (this.over === false && !this.parent()) {
21241 if (this.triggers === false) {
21246 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21247 var triggers = this.trigger ? this.trigger.split(' ') : [];
21248 Roo.each(triggers, function(trigger) {
21250 if (trigger == 'click') {
21251 on_el.on('click', this.toggle, this);
21252 } else if (trigger != 'manual') {
21253 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21254 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21256 on_el.on(eventIn ,this.enter, this);
21257 on_el.on(eventOut, this.leave, this);
21267 toggle : function () {
21268 this.hoverState == 'in' ? this.leave() : this.enter();
21271 enter : function () {
21273 clearTimeout(this.timeout);
21275 this.hoverState = 'in';
21277 if (!this.delay || !this.delay.show) {
21282 this.timeout = setTimeout(function () {
21283 if (_t.hoverState == 'in') {
21286 }, this.delay.show)
21289 leave : function() {
21290 clearTimeout(this.timeout);
21292 this.hoverState = 'out';
21294 if (!this.delay || !this.delay.hide) {
21299 this.timeout = setTimeout(function () {
21300 if (_t.hoverState == 'out') {
21303 }, this.delay.hide)
21307 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21308 * @param {string} (left|right|top|bottom) position
21310 show : function (on_el, placement)
21312 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21313 on_el = on_el || false; // default to false
21316 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21317 on_el = this.parent().el;
21318 } else if (this.over) {
21319 on_el = Roo.get(this.over);
21324 this.alignEl = Roo.get( on_el );
21327 this.render(document.body);
21333 if (this.title === false) {
21334 this.headerEl.hide();
21339 this.el.dom.style.display = 'block';
21342 if (this.alignEl) {
21343 this.updatePosition(this.placement, true);
21346 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21347 var es = this.el.getSize();
21348 var x = Roo.lib.Dom.getViewWidth()/2;
21349 var y = Roo.lib.Dom.getViewHeight()/2;
21350 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21355 //var arrow = this.el.select('.arrow',true).first();
21356 //arrow.set(align[2],
21358 this.el.addClass('in');
21362 this.hoverState = 'in';
21365 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21366 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21367 this.maskEl.dom.style.display = 'block';
21368 this.maskEl.addClass('show');
21370 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21372 this.fireEvent('show', this);
21376 * fire this manually after loading a grid in the table for example
21377 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21378 * @param {Boolean} try and move it if we cant get right position.
21380 updatePosition : function(placement, try_move)
21382 // allow for calling with no parameters
21383 placement = placement ? placement : this.placement;
21384 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21386 this.el.removeClass([
21387 'fade','top','bottom', 'left', 'right','in',
21388 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21390 this.el.addClass(placement + ' bs-popover-' + placement);
21392 if (!this.alignEl ) {
21396 switch (placement) {
21398 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21399 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21400 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21401 //normal display... or moved up/down.
21402 this.el.setXY(offset);
21403 var xy = this.alignEl.getAnchorXY('tr', false);
21405 this.arrowEl.setXY(xy);
21408 // continue through...
21409 return this.updatePosition('left', false);
21413 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21414 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21415 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21416 //normal display... or moved up/down.
21417 this.el.setXY(offset);
21418 var xy = this.alignEl.getAnchorXY('tl', false);
21419 xy[0]-=10;xy[1]+=5; // << fix me
21420 this.arrowEl.setXY(xy);
21424 return this.updatePosition('right', false);
21427 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21428 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21429 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21430 //normal display... or moved up/down.
21431 this.el.setXY(offset);
21432 var xy = this.alignEl.getAnchorXY('t', false);
21433 xy[1]-=10; // << fix me
21434 this.arrowEl.setXY(xy);
21438 return this.updatePosition('bottom', false);
21441 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21442 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21443 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21444 //normal display... or moved up/down.
21445 this.el.setXY(offset);
21446 var xy = this.alignEl.getAnchorXY('b', false);
21447 xy[1]+=2; // << fix me
21448 this.arrowEl.setXY(xy);
21452 return this.updatePosition('top', false);
21463 this.el.setXY([0,0]);
21464 this.el.removeClass('in');
21466 this.hoverState = null;
21467 this.maskEl.hide(); // always..
21468 this.fireEvent('hide', this);
21474 Roo.apply(Roo.bootstrap.Popover, {
21477 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21478 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21479 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21480 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21485 clickHander : false,
21489 onMouseDown : function(e)
21491 if (this.popups.length && !e.getTarget(".roo-popover")) {
21492 /// what is nothing is showing..
21501 register : function(popup)
21503 if (!Roo.bootstrap.Popover.clickHandler) {
21504 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21506 // hide other popups.
21507 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21508 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21509 this.hideAll(); //<< why?
21510 //this.popups.push(popup);
21512 hideAll : function()
21514 this.popups.forEach(function(p) {
21518 onShow : function() {
21519 Roo.bootstrap.Popover.popups.push(this);
21521 onHide : function() {
21522 Roo.bootstrap.Popover.popups.remove(this);
21528 * Card header - holder for the card header elements.
21533 * @class Roo.bootstrap.PopoverNav
21534 * @extends Roo.bootstrap.NavGroup
21535 * Bootstrap Popover header navigation class
21537 * Create a new Popover Header Navigation
21538 * @param {Object} config The config object
21541 Roo.bootstrap.PopoverNav = function(config){
21542 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21545 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21548 container_method : 'getPopoverHeader'
21566 * @class Roo.bootstrap.Progress
21567 * @extends Roo.bootstrap.Component
21568 * Bootstrap Progress class
21569 * @cfg {Boolean} striped striped of the progress bar
21570 * @cfg {Boolean} active animated of the progress bar
21574 * Create a new Progress
21575 * @param {Object} config The config object
21578 Roo.bootstrap.Progress = function(config){
21579 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21582 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21587 getAutoCreate : function(){
21595 cfg.cls += ' progress-striped';
21599 cfg.cls += ' active';
21618 * @class Roo.bootstrap.ProgressBar
21619 * @extends Roo.bootstrap.Component
21620 * Bootstrap ProgressBar class
21621 * @cfg {Number} aria_valuenow aria-value now
21622 * @cfg {Number} aria_valuemin aria-value min
21623 * @cfg {Number} aria_valuemax aria-value max
21624 * @cfg {String} label label for the progress bar
21625 * @cfg {String} panel (success | info | warning | danger )
21626 * @cfg {String} role role of the progress bar
21627 * @cfg {String} sr_only text
21631 * Create a new ProgressBar
21632 * @param {Object} config The config object
21635 Roo.bootstrap.ProgressBar = function(config){
21636 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21639 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21643 aria_valuemax : 100,
21649 getAutoCreate : function()
21654 cls: 'progress-bar',
21655 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21667 cfg.role = this.role;
21670 if(this.aria_valuenow){
21671 cfg['aria-valuenow'] = this.aria_valuenow;
21674 if(this.aria_valuemin){
21675 cfg['aria-valuemin'] = this.aria_valuemin;
21678 if(this.aria_valuemax){
21679 cfg['aria-valuemax'] = this.aria_valuemax;
21682 if(this.label && !this.sr_only){
21683 cfg.html = this.label;
21687 cfg.cls += ' progress-bar-' + this.panel;
21693 update : function(aria_valuenow)
21695 this.aria_valuenow = aria_valuenow;
21697 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21712 * @class Roo.bootstrap.TabGroup
21713 * @extends Roo.bootstrap.Column
21714 * Bootstrap Column class
21715 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21716 * @cfg {Boolean} carousel true to make the group behave like a carousel
21717 * @cfg {Boolean} bullets show bullets for the panels
21718 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21719 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21720 * @cfg {Boolean} showarrow (true|false) show arrow default true
21723 * Create a new TabGroup
21724 * @param {Object} config The config object
21727 Roo.bootstrap.TabGroup = function(config){
21728 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21730 this.navId = Roo.id();
21733 Roo.bootstrap.TabGroup.register(this);
21737 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21740 transition : false,
21745 slideOnTouch : false,
21748 getAutoCreate : function()
21750 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21752 cfg.cls += ' tab-content';
21754 if (this.carousel) {
21755 cfg.cls += ' carousel slide';
21758 cls : 'carousel-inner',
21762 if(this.bullets && !Roo.isTouch){
21765 cls : 'carousel-bullets',
21769 if(this.bullets_cls){
21770 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21777 cfg.cn[0].cn.push(bullets);
21780 if(this.showarrow){
21781 cfg.cn[0].cn.push({
21783 class : 'carousel-arrow',
21787 class : 'carousel-prev',
21791 class : 'fa fa-chevron-left'
21797 class : 'carousel-next',
21801 class : 'fa fa-chevron-right'
21814 initEvents: function()
21816 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21817 // this.el.on("touchstart", this.onTouchStart, this);
21820 if(this.autoslide){
21823 this.slideFn = window.setInterval(function() {
21824 _this.showPanelNext();
21828 if(this.showarrow){
21829 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21830 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21836 // onTouchStart : function(e, el, o)
21838 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21842 // this.showPanelNext();
21846 getChildContainer : function()
21848 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21852 * register a Navigation item
21853 * @param {Roo.bootstrap.NavItem} the navitem to add
21855 register : function(item)
21857 this.tabs.push( item);
21858 item.navId = this.navId; // not really needed..
21863 getActivePanel : function()
21866 Roo.each(this.tabs, function(t) {
21876 getPanelByName : function(n)
21879 Roo.each(this.tabs, function(t) {
21880 if (t.tabId == n) {
21888 indexOfPanel : function(p)
21891 Roo.each(this.tabs, function(t,i) {
21892 if (t.tabId == p.tabId) {
21901 * show a specific panel
21902 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21903 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21905 showPanel : function (pan)
21907 if(this.transition || typeof(pan) == 'undefined'){
21908 Roo.log("waiting for the transitionend");
21912 if (typeof(pan) == 'number') {
21913 pan = this.tabs[pan];
21916 if (typeof(pan) == 'string') {
21917 pan = this.getPanelByName(pan);
21920 var cur = this.getActivePanel();
21923 Roo.log('pan or acitve pan is undefined');
21927 if (pan.tabId == this.getActivePanel().tabId) {
21931 if (false === cur.fireEvent('beforedeactivate')) {
21935 if(this.bullets > 0 && !Roo.isTouch){
21936 this.setActiveBullet(this.indexOfPanel(pan));
21939 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21941 //class="carousel-item carousel-item-next carousel-item-left"
21943 this.transition = true;
21944 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21945 var lr = dir == 'next' ? 'left' : 'right';
21946 pan.el.addClass(dir); // or prev
21947 pan.el.addClass('carousel-item-' + dir); // or prev
21948 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21949 cur.el.addClass(lr); // or right
21950 pan.el.addClass(lr);
21951 cur.el.addClass('carousel-item-' +lr); // or right
21952 pan.el.addClass('carousel-item-' +lr);
21956 cur.el.on('transitionend', function() {
21957 Roo.log("trans end?");
21959 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21960 pan.setActive(true);
21962 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21963 cur.setActive(false);
21965 _this.transition = false;
21967 }, this, { single: true } );
21972 cur.setActive(false);
21973 pan.setActive(true);
21978 showPanelNext : function()
21980 var i = this.indexOfPanel(this.getActivePanel());
21982 if (i >= this.tabs.length - 1 && !this.autoslide) {
21986 if (i >= this.tabs.length - 1 && this.autoslide) {
21990 this.showPanel(this.tabs[i+1]);
21993 showPanelPrev : function()
21995 var i = this.indexOfPanel(this.getActivePanel());
21997 if (i < 1 && !this.autoslide) {
22001 if (i < 1 && this.autoslide) {
22002 i = this.tabs.length;
22005 this.showPanel(this.tabs[i-1]);
22009 addBullet: function()
22011 if(!this.bullets || Roo.isTouch){
22014 var ctr = this.el.select('.carousel-bullets',true).first();
22015 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22016 var bullet = ctr.createChild({
22017 cls : 'bullet bullet-' + i
22018 },ctr.dom.lastChild);
22023 bullet.on('click', (function(e, el, o, ii, t){
22025 e.preventDefault();
22027 this.showPanel(ii);
22029 if(this.autoslide && this.slideFn){
22030 clearInterval(this.slideFn);
22031 this.slideFn = window.setInterval(function() {
22032 _this.showPanelNext();
22036 }).createDelegate(this, [i, bullet], true));
22041 setActiveBullet : function(i)
22047 Roo.each(this.el.select('.bullet', true).elements, function(el){
22048 el.removeClass('selected');
22051 var bullet = this.el.select('.bullet-' + i, true).first();
22057 bullet.addClass('selected');
22068 Roo.apply(Roo.bootstrap.TabGroup, {
22072 * register a Navigation Group
22073 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22075 register : function(navgrp)
22077 this.groups[navgrp.navId] = navgrp;
22081 * fetch a Navigation Group based on the navigation ID
22082 * if one does not exist , it will get created.
22083 * @param {string} the navgroup to add
22084 * @returns {Roo.bootstrap.NavGroup} the navgroup
22086 get: function(navId) {
22087 if (typeof(this.groups[navId]) == 'undefined') {
22088 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22090 return this.groups[navId] ;
22105 * @class Roo.bootstrap.TabPanel
22106 * @extends Roo.bootstrap.Component
22107 * Bootstrap TabPanel class
22108 * @cfg {Boolean} active panel active
22109 * @cfg {String} html panel content
22110 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22111 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22112 * @cfg {String} href click to link..
22113 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22117 * Create a new TabPanel
22118 * @param {Object} config The config object
22121 Roo.bootstrap.TabPanel = function(config){
22122 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22126 * Fires when the active status changes
22127 * @param {Roo.bootstrap.TabPanel} this
22128 * @param {Boolean} state the new state
22133 * @event beforedeactivate
22134 * Fires before a tab is de-activated - can be used to do validation on a form.
22135 * @param {Roo.bootstrap.TabPanel} this
22136 * @return {Boolean} false if there is an error
22139 'beforedeactivate': true
22142 this.tabId = this.tabId || Roo.id();
22146 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22153 touchSlide : false,
22154 getAutoCreate : function(){
22159 // item is needed for carousel - not sure if it has any effect otherwise
22160 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22161 html: this.html || ''
22165 cfg.cls += ' active';
22169 cfg.tabId = this.tabId;
22177 initEvents: function()
22179 var p = this.parent();
22181 this.navId = this.navId || p.navId;
22183 if (typeof(this.navId) != 'undefined') {
22184 // not really needed.. but just in case.. parent should be a NavGroup.
22185 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22189 var i = tg.tabs.length - 1;
22191 if(this.active && tg.bullets > 0 && i < tg.bullets){
22192 tg.setActiveBullet(i);
22196 this.el.on('click', this.onClick, this);
22198 if(Roo.isTouch && this.touchSlide){
22199 this.el.on("touchstart", this.onTouchStart, this);
22200 this.el.on("touchmove", this.onTouchMove, this);
22201 this.el.on("touchend", this.onTouchEnd, this);
22206 onRender : function(ct, position)
22208 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22211 setActive : function(state)
22213 Roo.log("panel - set active " + this.tabId + "=" + state);
22215 this.active = state;
22217 this.el.removeClass('active');
22219 } else if (!this.el.hasClass('active')) {
22220 this.el.addClass('active');
22223 this.fireEvent('changed', this, state);
22226 onClick : function(e)
22228 e.preventDefault();
22230 if(!this.href.length){
22234 window.location.href = this.href;
22243 onTouchStart : function(e)
22245 this.swiping = false;
22247 this.startX = e.browserEvent.touches[0].clientX;
22248 this.startY = e.browserEvent.touches[0].clientY;
22251 onTouchMove : function(e)
22253 this.swiping = true;
22255 this.endX = e.browserEvent.touches[0].clientX;
22256 this.endY = e.browserEvent.touches[0].clientY;
22259 onTouchEnd : function(e)
22266 var tabGroup = this.parent();
22268 if(this.endX > this.startX){ // swiping right
22269 tabGroup.showPanelPrev();
22273 if(this.startX > this.endX){ // swiping left
22274 tabGroup.showPanelNext();
22293 * @class Roo.bootstrap.DateField
22294 * @extends Roo.bootstrap.Input
22295 * Bootstrap DateField class
22296 * @cfg {Number} weekStart default 0
22297 * @cfg {String} viewMode default empty, (months|years)
22298 * @cfg {String} minViewMode default empty, (months|years)
22299 * @cfg {Number} startDate default -Infinity
22300 * @cfg {Number} endDate default Infinity
22301 * @cfg {Boolean} todayHighlight default false
22302 * @cfg {Boolean} todayBtn default false
22303 * @cfg {Boolean} calendarWeeks default false
22304 * @cfg {Object} daysOfWeekDisabled default empty
22305 * @cfg {Boolean} singleMode default false (true | false)
22307 * @cfg {Boolean} keyboardNavigation default true
22308 * @cfg {String} language default en
22311 * Create a new DateField
22312 * @param {Object} config The config object
22315 Roo.bootstrap.DateField = function(config){
22316 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22320 * Fires when this field show.
22321 * @param {Roo.bootstrap.DateField} this
22322 * @param {Mixed} date The date value
22327 * Fires when this field hide.
22328 * @param {Roo.bootstrap.DateField} this
22329 * @param {Mixed} date The date value
22334 * Fires when select a date.
22335 * @param {Roo.bootstrap.DateField} this
22336 * @param {Mixed} date The date value
22340 * @event beforeselect
22341 * Fires when before select a date.
22342 * @param {Roo.bootstrap.DateField} this
22343 * @param {Mixed} date The date value
22345 beforeselect : true
22349 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22352 * @cfg {String} format
22353 * The default date format string which can be overriden for localization support. The format must be
22354 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22358 * @cfg {String} altFormats
22359 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22360 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22362 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22370 todayHighlight : false,
22376 keyboardNavigation: true,
22378 calendarWeeks: false,
22380 startDate: -Infinity,
22384 daysOfWeekDisabled: [],
22388 singleMode : false,
22390 UTCDate: function()
22392 return new Date(Date.UTC.apply(Date, arguments));
22395 UTCToday: function()
22397 var today = new Date();
22398 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22401 getDate: function() {
22402 var d = this.getUTCDate();
22403 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22406 getUTCDate: function() {
22410 setDate: function(d) {
22411 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22414 setUTCDate: function(d) {
22416 this.setValue(this.formatDate(this.date));
22419 onRender: function(ct, position)
22422 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22424 this.language = this.language || 'en';
22425 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22426 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22428 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22429 this.format = this.format || 'm/d/y';
22430 this.isInline = false;
22431 this.isInput = true;
22432 this.component = this.el.select('.add-on', true).first() || false;
22433 this.component = (this.component && this.component.length === 0) ? false : this.component;
22434 this.hasInput = this.component && this.inputEl().length;
22436 if (typeof(this.minViewMode === 'string')) {
22437 switch (this.minViewMode) {
22439 this.minViewMode = 1;
22442 this.minViewMode = 2;
22445 this.minViewMode = 0;
22450 if (typeof(this.viewMode === 'string')) {
22451 switch (this.viewMode) {
22464 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22466 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22468 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22470 this.picker().on('mousedown', this.onMousedown, this);
22471 this.picker().on('click', this.onClick, this);
22473 this.picker().addClass('datepicker-dropdown');
22475 this.startViewMode = this.viewMode;
22477 if(this.singleMode){
22478 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22479 v.setVisibilityMode(Roo.Element.DISPLAY);
22483 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22484 v.setStyle('width', '189px');
22488 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22489 if(!this.calendarWeeks){
22494 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22495 v.attr('colspan', function(i, val){
22496 return parseInt(val) + 1;
22501 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22503 this.setStartDate(this.startDate);
22504 this.setEndDate(this.endDate);
22506 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22513 if(this.isInline) {
22518 picker : function()
22520 return this.pickerEl;
22521 // return this.el.select('.datepicker', true).first();
22524 fillDow: function()
22526 var dowCnt = this.weekStart;
22535 if(this.calendarWeeks){
22543 while (dowCnt < this.weekStart + 7) {
22547 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22551 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22554 fillMonths: function()
22557 var months = this.picker().select('>.datepicker-months td', true).first();
22559 months.dom.innerHTML = '';
22565 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22568 months.createChild(month);
22575 this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
22577 if (this.date < this.startDate) {
22578 this.viewDate = new Date(this.startDate);
22579 } else if (this.date > this.endDate) {
22580 this.viewDate = new Date(this.endDate);
22582 this.viewDate = new Date(this.date);
22590 var d = new Date(this.viewDate),
22591 year = d.getUTCFullYear(),
22592 month = d.getUTCMonth(),
22593 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22594 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22595 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22596 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22597 currentDate = this.date && this.date.valueOf(),
22598 today = this.UTCToday();
22600 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22602 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22604 // this.picker.select('>tfoot th.today').
22605 // .text(dates[this.language].today)
22606 // .toggle(this.todayBtn !== false);
22608 this.updateNavArrows();
22611 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22613 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22615 prevMonth.setUTCDate(day);
22617 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22619 var nextMonth = new Date(prevMonth);
22621 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22623 nextMonth = nextMonth.valueOf();
22625 var fillMonths = false;
22627 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22629 while(prevMonth.valueOf() <= nextMonth) {
22632 if (prevMonth.getUTCDay() === this.weekStart) {
22634 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22642 if(this.calendarWeeks){
22643 // ISO 8601: First week contains first thursday.
22644 // ISO also states week starts on Monday, but we can be more abstract here.
22646 // Start of current week: based on weekstart/current date
22647 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22648 // Thursday of this week
22649 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22650 // First Thursday of year, year from thursday
22651 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22652 // Calendar week: ms between thursdays, div ms per day, div 7 days
22653 calWeek = (th - yth) / 864e5 / 7 + 1;
22655 fillMonths.cn.push({
22663 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22665 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22668 if (this.todayHighlight &&
22669 prevMonth.getUTCFullYear() == today.getFullYear() &&
22670 prevMonth.getUTCMonth() == today.getMonth() &&
22671 prevMonth.getUTCDate() == today.getDate()) {
22672 clsName += ' today';
22675 if (currentDate && prevMonth.valueOf() === currentDate) {
22676 clsName += ' active';
22679 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22680 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22681 clsName += ' disabled';
22684 fillMonths.cn.push({
22686 cls: 'day ' + clsName,
22687 html: prevMonth.getDate()
22690 prevMonth.setDate(prevMonth.getDate()+1);
22693 var currentYear = this.date && this.date.getUTCFullYear();
22694 var currentMonth = this.date && this.date.getUTCMonth();
22696 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22698 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22699 v.removeClass('active');
22701 if(currentYear === year && k === currentMonth){
22702 v.addClass('active');
22705 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22706 v.addClass('disabled');
22712 year = parseInt(year/10, 10) * 10;
22714 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22716 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22719 for (var i = -1; i < 11; i++) {
22720 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22722 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22730 showMode: function(dir)
22733 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22736 Roo.each(this.picker().select('>div',true).elements, function(v){
22737 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22740 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22745 if(this.isInline) {
22749 this.picker().removeClass(['bottom', 'top']);
22751 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22753 * place to the top of element!
22757 this.picker().addClass('top');
22758 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22763 this.picker().addClass('bottom');
22765 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22768 parseDate : function(value)
22770 if(!value || value instanceof Date){
22773 var v = Date.parseDate(value, this.format);
22774 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22775 v = Date.parseDate(value, 'Y-m-d');
22777 if(!v && this.altFormats){
22778 if(!this.altFormatsArray){
22779 this.altFormatsArray = this.altFormats.split("|");
22781 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22782 v = Date.parseDate(value, this.altFormatsArray[i]);
22788 formatDate : function(date, fmt)
22790 return (!date || !(date instanceof Date)) ?
22791 date : date.dateFormat(fmt || this.format);
22794 onFocus : function()
22796 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22800 onBlur : function()
22802 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22804 var d = this.inputEl().getValue();
22811 showPopup : function()
22813 this.picker().show();
22817 this.fireEvent('showpopup', this, this.date);
22820 hidePopup : function()
22822 if(this.isInline) {
22825 this.picker().hide();
22826 this.viewMode = this.startViewMode;
22829 this.fireEvent('hidepopup', this, this.date);
22833 onMousedown: function(e)
22835 e.stopPropagation();
22836 e.preventDefault();
22841 Roo.bootstrap.DateField.superclass.keyup.call(this);
22845 setValue: function(v)
22847 if(this.fireEvent('beforeselect', this, v) !== false){
22848 var d = new Date(this.parseDate(v) ).clearTime();
22850 if(isNaN(d.getTime())){
22851 this.date = this.viewDate = '';
22852 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22856 v = this.formatDate(d);
22858 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22860 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22864 this.fireEvent('select', this, this.date);
22868 getValue: function()
22870 return this.formatDate(this.date);
22873 fireKey: function(e)
22875 if (!this.picker().isVisible()){
22876 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22882 var dateChanged = false,
22884 newDate, newViewDate;
22889 e.preventDefault();
22893 if (!this.keyboardNavigation) {
22896 dir = e.keyCode == 37 ? -1 : 1;
22899 newDate = this.moveYear(this.date, dir);
22900 newViewDate = this.moveYear(this.viewDate, dir);
22901 } else if (e.shiftKey){
22902 newDate = this.moveMonth(this.date, dir);
22903 newViewDate = this.moveMonth(this.viewDate, dir);
22905 newDate = new Date(this.date);
22906 newDate.setUTCDate(this.date.getUTCDate() + dir);
22907 newViewDate = new Date(this.viewDate);
22908 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22910 if (this.dateWithinRange(newDate)){
22911 this.date = newDate;
22912 this.viewDate = newViewDate;
22913 this.setValue(this.formatDate(this.date));
22915 e.preventDefault();
22916 dateChanged = true;
22921 if (!this.keyboardNavigation) {
22924 dir = e.keyCode == 38 ? -1 : 1;
22926 newDate = this.moveYear(this.date, dir);
22927 newViewDate = this.moveYear(this.viewDate, dir);
22928 } else if (e.shiftKey){
22929 newDate = this.moveMonth(this.date, dir);
22930 newViewDate = this.moveMonth(this.viewDate, dir);
22932 newDate = new Date(this.date);
22933 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22934 newViewDate = new Date(this.viewDate);
22935 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22937 if (this.dateWithinRange(newDate)){
22938 this.date = newDate;
22939 this.viewDate = newViewDate;
22940 this.setValue(this.formatDate(this.date));
22942 e.preventDefault();
22943 dateChanged = true;
22947 this.setValue(this.formatDate(this.date));
22949 e.preventDefault();
22952 this.setValue(this.formatDate(this.date));
22966 onClick: function(e)
22968 e.stopPropagation();
22969 e.preventDefault();
22971 var target = e.getTarget();
22973 if(target.nodeName.toLowerCase() === 'i'){
22974 target = Roo.get(target).dom.parentNode;
22977 var nodeName = target.nodeName;
22978 var className = target.className;
22979 var html = target.innerHTML;
22980 //Roo.log(nodeName);
22982 switch(nodeName.toLowerCase()) {
22984 switch(className) {
22990 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22991 switch(this.viewMode){
22993 this.viewDate = this.moveMonth(this.viewDate, dir);
22997 this.viewDate = this.moveYear(this.viewDate, dir);
23003 var date = new Date();
23004 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23006 this.setValue(this.formatDate(this.date));
23013 if (className.indexOf('disabled') < 0) {
23014 if (!this.viewDate) {
23015 this.viewDate = new Date();
23017 this.viewDate.setUTCDate(1);
23018 if (className.indexOf('month') > -1) {
23019 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23021 var year = parseInt(html, 10) || 0;
23022 this.viewDate.setUTCFullYear(year);
23026 if(this.singleMode){
23027 this.setValue(this.formatDate(this.viewDate));
23038 //Roo.log(className);
23039 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23040 var day = parseInt(html, 10) || 1;
23041 var year = (this.viewDate || new Date()).getUTCFullYear(),
23042 month = (this.viewDate || new Date()).getUTCMonth();
23044 if (className.indexOf('old') > -1) {
23051 } else if (className.indexOf('new') > -1) {
23059 //Roo.log([year,month,day]);
23060 this.date = this.UTCDate(year, month, day,0,0,0,0);
23061 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23063 //Roo.log(this.formatDate(this.date));
23064 this.setValue(this.formatDate(this.date));
23071 setStartDate: function(startDate)
23073 this.startDate = startDate || -Infinity;
23074 if (this.startDate !== -Infinity) {
23075 this.startDate = this.parseDate(this.startDate);
23078 this.updateNavArrows();
23081 setEndDate: function(endDate)
23083 this.endDate = endDate || Infinity;
23084 if (this.endDate !== Infinity) {
23085 this.endDate = this.parseDate(this.endDate);
23088 this.updateNavArrows();
23091 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23093 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23094 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23095 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23097 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23098 return parseInt(d, 10);
23101 this.updateNavArrows();
23104 updateNavArrows: function()
23106 if(this.singleMode){
23110 var d = new Date(this.viewDate),
23111 year = d.getUTCFullYear(),
23112 month = d.getUTCMonth();
23114 Roo.each(this.picker().select('.prev', true).elements, function(v){
23116 switch (this.viewMode) {
23119 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23125 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23132 Roo.each(this.picker().select('.next', true).elements, function(v){
23134 switch (this.viewMode) {
23137 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23143 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23151 moveMonth: function(date, dir)
23156 var new_date = new Date(date.valueOf()),
23157 day = new_date.getUTCDate(),
23158 month = new_date.getUTCMonth(),
23159 mag = Math.abs(dir),
23161 dir = dir > 0 ? 1 : -1;
23164 // If going back one month, make sure month is not current month
23165 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23167 return new_date.getUTCMonth() == month;
23169 // If going forward one month, make sure month is as expected
23170 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23172 return new_date.getUTCMonth() != new_month;
23174 new_month = month + dir;
23175 new_date.setUTCMonth(new_month);
23176 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23177 if (new_month < 0 || new_month > 11) {
23178 new_month = (new_month + 12) % 12;
23181 // For magnitudes >1, move one month at a time...
23182 for (var i=0; i<mag; i++) {
23183 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23184 new_date = this.moveMonth(new_date, dir);
23186 // ...then reset the day, keeping it in the new month
23187 new_month = new_date.getUTCMonth();
23188 new_date.setUTCDate(day);
23190 return new_month != new_date.getUTCMonth();
23193 // Common date-resetting loop -- if date is beyond end of month, make it
23196 new_date.setUTCDate(--day);
23197 new_date.setUTCMonth(new_month);
23202 moveYear: function(date, dir)
23204 return this.moveMonth(date, dir*12);
23207 dateWithinRange: function(date)
23209 return date >= this.startDate && date <= this.endDate;
23215 this.picker().remove();
23218 validateValue : function(value)
23220 if(this.getVisibilityEl().hasClass('hidden')){
23224 if(value.length < 1) {
23225 if(this.allowBlank){
23231 if(value.length < this.minLength){
23234 if(value.length > this.maxLength){
23238 var vt = Roo.form.VTypes;
23239 if(!vt[this.vtype](value, this)){
23243 if(typeof this.validator == "function"){
23244 var msg = this.validator(value);
23250 if(this.regex && !this.regex.test(value)){
23254 if(typeof(this.parseDate(value)) == 'undefined'){
23258 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23262 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23272 this.date = this.viewDate = '';
23274 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23279 Roo.apply(Roo.bootstrap.DateField, {
23290 html: '<i class="fa fa-arrow-left"/>'
23300 html: '<i class="fa fa-arrow-right"/>'
23342 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23343 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23344 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23345 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23346 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23359 navFnc: 'FullYear',
23364 navFnc: 'FullYear',
23369 Roo.apply(Roo.bootstrap.DateField, {
23373 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23377 cls: 'datepicker-days',
23381 cls: 'table-condensed',
23383 Roo.bootstrap.DateField.head,
23387 Roo.bootstrap.DateField.footer
23394 cls: 'datepicker-months',
23398 cls: 'table-condensed',
23400 Roo.bootstrap.DateField.head,
23401 Roo.bootstrap.DateField.content,
23402 Roo.bootstrap.DateField.footer
23409 cls: 'datepicker-years',
23413 cls: 'table-condensed',
23415 Roo.bootstrap.DateField.head,
23416 Roo.bootstrap.DateField.content,
23417 Roo.bootstrap.DateField.footer
23436 * @class Roo.bootstrap.TimeField
23437 * @extends Roo.bootstrap.Input
23438 * Bootstrap DateField class
23442 * Create a new TimeField
23443 * @param {Object} config The config object
23446 Roo.bootstrap.TimeField = function(config){
23447 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23451 * Fires when this field show.
23452 * @param {Roo.bootstrap.DateField} thisthis
23453 * @param {Mixed} date The date value
23458 * Fires when this field hide.
23459 * @param {Roo.bootstrap.DateField} this
23460 * @param {Mixed} date The date value
23465 * Fires when select a date.
23466 * @param {Roo.bootstrap.DateField} this
23467 * @param {Mixed} date The date value
23473 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23476 * @cfg {String} format
23477 * The default time format string which can be overriden for localization support. The format must be
23478 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23482 getAutoCreate : function()
23484 this.after = '<i class="fa far fa-clock"></i>';
23485 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23489 onRender: function(ct, position)
23492 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23494 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23496 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23498 this.pop = this.picker().select('>.datepicker-time',true).first();
23499 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23501 this.picker().on('mousedown', this.onMousedown, this);
23502 this.picker().on('click', this.onClick, this);
23504 this.picker().addClass('datepicker-dropdown');
23509 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23510 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23511 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23512 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23513 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23514 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23518 fireKey: function(e){
23519 if (!this.picker().isVisible()){
23520 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23526 e.preventDefault();
23534 this.onTogglePeriod();
23537 this.onIncrementMinutes();
23540 this.onDecrementMinutes();
23549 onClick: function(e) {
23550 e.stopPropagation();
23551 e.preventDefault();
23554 picker : function()
23556 return this.pickerEl;
23559 fillTime: function()
23561 var time = this.pop.select('tbody', true).first();
23563 time.dom.innerHTML = '';
23578 cls: 'hours-up fa fas fa-chevron-up'
23598 cls: 'minutes-up fa fas fa-chevron-up'
23619 cls: 'timepicker-hour',
23634 cls: 'timepicker-minute',
23649 cls: 'btn btn-primary period',
23671 cls: 'hours-down fa fas fa-chevron-down'
23691 cls: 'minutes-down fa fas fa-chevron-down'
23709 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23716 var hours = this.time.getHours();
23717 var minutes = this.time.getMinutes();
23730 hours = hours - 12;
23734 hours = '0' + hours;
23738 minutes = '0' + minutes;
23741 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23742 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23743 this.pop.select('button', true).first().dom.innerHTML = period;
23749 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23751 var cls = ['bottom'];
23753 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23760 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23764 //this.picker().setXY(20000,20000);
23765 this.picker().addClass(cls.join('-'));
23769 Roo.each(cls, function(c){
23774 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23775 //_this.picker().setTop(_this.inputEl().getHeight());
23779 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23781 //_this.picker().setTop(0 - _this.picker().getHeight());
23786 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23790 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23798 onFocus : function()
23800 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23804 onBlur : function()
23806 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23812 this.picker().show();
23817 this.fireEvent('show', this, this.date);
23822 this.picker().hide();
23825 this.fireEvent('hide', this, this.date);
23828 setTime : function()
23831 this.setValue(this.time.format(this.format));
23833 this.fireEvent('select', this, this.date);
23838 onMousedown: function(e){
23839 e.stopPropagation();
23840 e.preventDefault();
23843 onIncrementHours: function()
23845 Roo.log('onIncrementHours');
23846 this.time = this.time.add(Date.HOUR, 1);
23851 onDecrementHours: function()
23853 Roo.log('onDecrementHours');
23854 this.time = this.time.add(Date.HOUR, -1);
23858 onIncrementMinutes: function()
23860 Roo.log('onIncrementMinutes');
23861 this.time = this.time.add(Date.MINUTE, 1);
23865 onDecrementMinutes: function()
23867 Roo.log('onDecrementMinutes');
23868 this.time = this.time.add(Date.MINUTE, -1);
23872 onTogglePeriod: function()
23874 Roo.log('onTogglePeriod');
23875 this.time = this.time.add(Date.HOUR, 12);
23883 Roo.apply(Roo.bootstrap.TimeField, {
23887 cls: 'datepicker dropdown-menu',
23891 cls: 'datepicker-time',
23895 cls: 'table-condensed',
23924 cls: 'btn btn-info ok',
23952 * @class Roo.bootstrap.MonthField
23953 * @extends Roo.bootstrap.Input
23954 * Bootstrap MonthField class
23956 * @cfg {String} language default en
23959 * Create a new MonthField
23960 * @param {Object} config The config object
23963 Roo.bootstrap.MonthField = function(config){
23964 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23969 * Fires when this field show.
23970 * @param {Roo.bootstrap.MonthField} this
23971 * @param {Mixed} date The date value
23976 * Fires when this field hide.
23977 * @param {Roo.bootstrap.MonthField} this
23978 * @param {Mixed} date The date value
23983 * Fires when select a date.
23984 * @param {Roo.bootstrap.MonthField} this
23985 * @param {String} oldvalue The old value
23986 * @param {String} newvalue The new value
23992 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23994 onRender: function(ct, position)
23997 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23999 this.language = this.language || 'en';
24000 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24001 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24003 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24004 this.isInline = false;
24005 this.isInput = true;
24006 this.component = this.el.select('.add-on', true).first() || false;
24007 this.component = (this.component && this.component.length === 0) ? false : this.component;
24008 this.hasInput = this.component && this.inputEL().length;
24010 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24012 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24014 this.picker().on('mousedown', this.onMousedown, this);
24015 this.picker().on('click', this.onClick, this);
24017 this.picker().addClass('datepicker-dropdown');
24019 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24020 v.setStyle('width', '189px');
24027 if(this.isInline) {
24033 setValue: function(v, suppressEvent)
24035 var o = this.getValue();
24037 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24041 if(suppressEvent !== true){
24042 this.fireEvent('select', this, o, v);
24047 getValue: function()
24052 onClick: function(e)
24054 e.stopPropagation();
24055 e.preventDefault();
24057 var target = e.getTarget();
24059 if(target.nodeName.toLowerCase() === 'i'){
24060 target = Roo.get(target).dom.parentNode;
24063 var nodeName = target.nodeName;
24064 var className = target.className;
24065 var html = target.innerHTML;
24067 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24071 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24073 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24079 picker : function()
24081 return this.pickerEl;
24084 fillMonths: function()
24087 var months = this.picker().select('>.datepicker-months td', true).first();
24089 months.dom.innerHTML = '';
24095 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24098 months.createChild(month);
24107 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24108 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24111 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24112 e.removeClass('active');
24114 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24115 e.addClass('active');
24122 if(this.isInline) {
24126 this.picker().removeClass(['bottom', 'top']);
24128 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24130 * place to the top of element!
24134 this.picker().addClass('top');
24135 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24140 this.picker().addClass('bottom');
24142 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24145 onFocus : function()
24147 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24151 onBlur : function()
24153 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24155 var d = this.inputEl().getValue();
24164 this.picker().show();
24165 this.picker().select('>.datepicker-months', true).first().show();
24169 this.fireEvent('show', this, this.date);
24174 if(this.isInline) {
24177 this.picker().hide();
24178 this.fireEvent('hide', this, this.date);
24182 onMousedown: function(e)
24184 e.stopPropagation();
24185 e.preventDefault();
24190 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24194 fireKey: function(e)
24196 if (!this.picker().isVisible()){
24197 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24208 e.preventDefault();
24212 dir = e.keyCode == 37 ? -1 : 1;
24214 this.vIndex = this.vIndex + dir;
24216 if(this.vIndex < 0){
24220 if(this.vIndex > 11){
24224 if(isNaN(this.vIndex)){
24228 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24234 dir = e.keyCode == 38 ? -1 : 1;
24236 this.vIndex = this.vIndex + dir * 4;
24238 if(this.vIndex < 0){
24242 if(this.vIndex > 11){
24246 if(isNaN(this.vIndex)){
24250 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24255 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24256 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24260 e.preventDefault();
24263 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24264 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24280 this.picker().remove();
24285 Roo.apply(Roo.bootstrap.MonthField, {
24304 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24305 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24310 Roo.apply(Roo.bootstrap.MonthField, {
24314 cls: 'datepicker dropdown-menu roo-dynamic',
24318 cls: 'datepicker-months',
24322 cls: 'table-condensed',
24324 Roo.bootstrap.DateField.content
24344 * @class Roo.bootstrap.CheckBox
24345 * @extends Roo.bootstrap.Input
24346 * Bootstrap CheckBox class
24348 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24349 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24350 * @cfg {String} boxLabel The text that appears beside the checkbox
24351 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24352 * @cfg {Boolean} checked initnal the element
24353 * @cfg {Boolean} inline inline the element (default false)
24354 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24355 * @cfg {String} tooltip label tooltip
24358 * Create a new CheckBox
24359 * @param {Object} config The config object
24362 Roo.bootstrap.CheckBox = function(config){
24363 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24368 * Fires when the element is checked or unchecked.
24369 * @param {Roo.bootstrap.CheckBox} this This input
24370 * @param {Boolean} checked The new checked value
24375 * Fires when the element is click.
24376 * @param {Roo.bootstrap.CheckBox} this This input
24383 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24385 inputType: 'checkbox',
24394 // checkbox success does not make any sense really..
24399 getAutoCreate : function()
24401 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24407 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24410 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24416 type : this.inputType,
24417 value : this.inputValue,
24418 cls : 'roo-' + this.inputType, //'form-box',
24419 placeholder : this.placeholder || ''
24423 if(this.inputType != 'radio'){
24427 cls : 'roo-hidden-value',
24428 value : this.checked ? this.inputValue : this.valueOff
24433 if (this.weight) { // Validity check?
24434 cfg.cls += " " + this.inputType + "-" + this.weight;
24437 if (this.disabled) {
24438 input.disabled=true;
24442 input.checked = this.checked;
24447 input.name = this.name;
24449 if(this.inputType != 'radio'){
24450 hidden.name = this.name;
24451 input.name = '_hidden_' + this.name;
24456 input.cls += ' input-' + this.size;
24461 ['xs','sm','md','lg'].map(function(size){
24462 if (settings[size]) {
24463 cfg.cls += ' col-' + size + '-' + settings[size];
24467 var inputblock = input;
24469 if (this.before || this.after) {
24472 cls : 'input-group',
24477 inputblock.cn.push({
24479 cls : 'input-group-addon',
24484 inputblock.cn.push(input);
24486 if(this.inputType != 'radio'){
24487 inputblock.cn.push(hidden);
24491 inputblock.cn.push({
24493 cls : 'input-group-addon',
24499 var boxLabelCfg = false;
24505 //'for': id, // box label is handled by onclick - so no for...
24507 html: this.boxLabel
24510 boxLabelCfg.tooltip = this.tooltip;
24516 if (align ==='left' && this.fieldLabel.length) {
24517 // Roo.log("left and has label");
24522 cls : 'control-label',
24523 html : this.fieldLabel
24534 cfg.cn[1].cn.push(boxLabelCfg);
24537 if(this.labelWidth > 12){
24538 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24541 if(this.labelWidth < 13 && this.labelmd == 0){
24542 this.labelmd = this.labelWidth;
24545 if(this.labellg > 0){
24546 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24547 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24550 if(this.labelmd > 0){
24551 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24552 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24555 if(this.labelsm > 0){
24556 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24557 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24560 if(this.labelxs > 0){
24561 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24562 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24565 } else if ( this.fieldLabel.length) {
24566 // Roo.log(" label");
24570 tag: this.boxLabel ? 'span' : 'label',
24572 cls: 'control-label box-input-label',
24573 //cls : 'input-group-addon',
24574 html : this.fieldLabel
24581 cfg.cn.push(boxLabelCfg);
24586 // Roo.log(" no label && no align");
24587 cfg.cn = [ inputblock ] ;
24589 cfg.cn.push(boxLabelCfg);
24597 if(this.inputType != 'radio'){
24598 cfg.cn.push(hidden);
24606 * return the real input element.
24608 inputEl: function ()
24610 return this.el.select('input.roo-' + this.inputType,true).first();
24612 hiddenEl: function ()
24614 return this.el.select('input.roo-hidden-value',true).first();
24617 labelEl: function()
24619 return this.el.select('label.control-label',true).first();
24621 /* depricated... */
24625 return this.labelEl();
24628 boxLabelEl: function()
24630 return this.el.select('label.box-label',true).first();
24633 initEvents : function()
24635 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24637 this.inputEl().on('click', this.onClick, this);
24639 if (this.boxLabel) {
24640 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24643 this.startValue = this.getValue();
24646 Roo.bootstrap.CheckBox.register(this);
24650 onClick : function(e)
24652 if(this.fireEvent('click', this, e) !== false){
24653 this.setChecked(!this.checked);
24658 setChecked : function(state,suppressEvent)
24660 this.startValue = this.getValue();
24662 if(this.inputType == 'radio'){
24664 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24665 e.dom.checked = false;
24668 this.inputEl().dom.checked = true;
24670 this.inputEl().dom.value = this.inputValue;
24672 if(suppressEvent !== true){
24673 this.fireEvent('check', this, true);
24681 this.checked = state;
24683 this.inputEl().dom.checked = state;
24686 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24688 if(suppressEvent !== true){
24689 this.fireEvent('check', this, state);
24695 getValue : function()
24697 if(this.inputType == 'radio'){
24698 return this.getGroupValue();
24701 return this.hiddenEl().dom.value;
24705 getGroupValue : function()
24707 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24711 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24714 setValue : function(v,suppressEvent)
24716 if(this.inputType == 'radio'){
24717 this.setGroupValue(v, suppressEvent);
24721 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24726 setGroupValue : function(v, suppressEvent)
24728 this.startValue = this.getValue();
24730 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24731 e.dom.checked = false;
24733 if(e.dom.value == v){
24734 e.dom.checked = true;
24738 if(suppressEvent !== true){
24739 this.fireEvent('check', this, true);
24747 validate : function()
24749 if(this.getVisibilityEl().hasClass('hidden')){
24755 (this.inputType == 'radio' && this.validateRadio()) ||
24756 (this.inputType == 'checkbox' && this.validateCheckbox())
24762 this.markInvalid();
24766 validateRadio : function()
24768 if(this.getVisibilityEl().hasClass('hidden')){
24772 if(this.allowBlank){
24778 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24779 if(!e.dom.checked){
24791 validateCheckbox : function()
24794 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24795 //return (this.getValue() == this.inputValue) ? true : false;
24798 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24806 for(var i in group){
24807 if(group[i].el.isVisible(true)){
24815 for(var i in group){
24820 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24827 * Mark this field as valid
24829 markValid : function()
24833 this.fireEvent('valid', this);
24835 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24838 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24845 if(this.inputType == 'radio'){
24846 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24847 var fg = e.findParent('.form-group', false, true);
24848 if (Roo.bootstrap.version == 3) {
24849 fg.removeClass([_this.invalidClass, _this.validClass]);
24850 fg.addClass(_this.validClass);
24852 fg.removeClass(['is-valid', 'is-invalid']);
24853 fg.addClass('is-valid');
24861 var fg = this.el.findParent('.form-group', false, true);
24862 if (Roo.bootstrap.version == 3) {
24863 fg.removeClass([this.invalidClass, this.validClass]);
24864 fg.addClass(this.validClass);
24866 fg.removeClass(['is-valid', 'is-invalid']);
24867 fg.addClass('is-valid');
24872 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24878 for(var i in group){
24879 var fg = group[i].el.findParent('.form-group', false, true);
24880 if (Roo.bootstrap.version == 3) {
24881 fg.removeClass([this.invalidClass, this.validClass]);
24882 fg.addClass(this.validClass);
24884 fg.removeClass(['is-valid', 'is-invalid']);
24885 fg.addClass('is-valid');
24891 * Mark this field as invalid
24892 * @param {String} msg The validation message
24894 markInvalid : function(msg)
24896 if(this.allowBlank){
24902 this.fireEvent('invalid', this, msg);
24904 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24907 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24911 label.markInvalid();
24914 if(this.inputType == 'radio'){
24916 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24917 var fg = e.findParent('.form-group', false, true);
24918 if (Roo.bootstrap.version == 3) {
24919 fg.removeClass([_this.invalidClass, _this.validClass]);
24920 fg.addClass(_this.invalidClass);
24922 fg.removeClass(['is-invalid', 'is-valid']);
24923 fg.addClass('is-invalid');
24931 var fg = this.el.findParent('.form-group', false, true);
24932 if (Roo.bootstrap.version == 3) {
24933 fg.removeClass([_this.invalidClass, _this.validClass]);
24934 fg.addClass(_this.invalidClass);
24936 fg.removeClass(['is-invalid', 'is-valid']);
24937 fg.addClass('is-invalid');
24942 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24948 for(var i in group){
24949 var fg = group[i].el.findParent('.form-group', false, true);
24950 if (Roo.bootstrap.version == 3) {
24951 fg.removeClass([_this.invalidClass, _this.validClass]);
24952 fg.addClass(_this.invalidClass);
24954 fg.removeClass(['is-invalid', 'is-valid']);
24955 fg.addClass('is-invalid');
24961 clearInvalid : function()
24963 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24965 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24967 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24969 if (label && label.iconEl) {
24970 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24971 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24975 disable : function()
24977 if(this.inputType != 'radio'){
24978 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24985 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24986 _this.getActionEl().addClass(this.disabledClass);
24987 e.dom.disabled = true;
24991 this.disabled = true;
24992 this.fireEvent("disable", this);
24996 enable : function()
24998 if(this.inputType != 'radio'){
24999 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25006 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25007 _this.getActionEl().removeClass(this.disabledClass);
25008 e.dom.disabled = false;
25012 this.disabled = false;
25013 this.fireEvent("enable", this);
25017 setBoxLabel : function(v)
25022 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25028 Roo.apply(Roo.bootstrap.CheckBox, {
25033 * register a CheckBox Group
25034 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25036 register : function(checkbox)
25038 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25039 this.groups[checkbox.groupId] = {};
25042 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25046 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25050 * fetch a CheckBox Group based on the group ID
25051 * @param {string} the group ID
25052 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25054 get: function(groupId) {
25055 if (typeof(this.groups[groupId]) == 'undefined') {
25059 return this.groups[groupId] ;
25072 * @class Roo.bootstrap.Radio
25073 * @extends Roo.bootstrap.Component
25074 * Bootstrap Radio class
25075 * @cfg {String} boxLabel - the label associated
25076 * @cfg {String} value - the value of radio
25079 * Create a new Radio
25080 * @param {Object} config The config object
25082 Roo.bootstrap.Radio = function(config){
25083 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25087 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25093 getAutoCreate : function()
25097 cls : 'form-group radio',
25102 html : this.boxLabel
25110 initEvents : function()
25112 this.parent().register(this);
25114 this.el.on('click', this.onClick, this);
25118 onClick : function(e)
25120 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25121 this.setChecked(true);
25125 setChecked : function(state, suppressEvent)
25127 this.parent().setValue(this.value, suppressEvent);
25131 setBoxLabel : function(v)
25136 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25151 * @class Roo.bootstrap.SecurePass
25152 * @extends Roo.bootstrap.Input
25153 * Bootstrap SecurePass class
25157 * Create a new SecurePass
25158 * @param {Object} config The config object
25161 Roo.bootstrap.SecurePass = function (config) {
25162 // these go here, so the translation tool can replace them..
25164 PwdEmpty: "Please type a password, and then retype it to confirm.",
25165 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25166 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25167 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25168 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25169 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25170 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25171 TooWeak: "Your password is Too Weak."
25173 this.meterLabel = "Password strength:";
25174 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25175 this.meterClass = [
25176 "roo-password-meter-tooweak",
25177 "roo-password-meter-weak",
25178 "roo-password-meter-medium",
25179 "roo-password-meter-strong",
25180 "roo-password-meter-grey"
25185 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25188 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25190 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25192 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25193 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25194 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25195 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25196 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25197 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25198 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25208 * @cfg {String/Object} Label for the strength meter (defaults to
25209 * 'Password strength:')
25214 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25215 * ['Weak', 'Medium', 'Strong'])
25218 pwdStrengths: false,
25231 initEvents: function ()
25233 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25235 if (this.el.is('input[type=password]') && Roo.isSafari) {
25236 this.el.on('keydown', this.SafariOnKeyDown, this);
25239 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25242 onRender: function (ct, position)
25244 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25245 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25246 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25248 this.trigger.createChild({
25253 cls: 'roo-password-meter-grey col-xs-12',
25256 //width: this.meterWidth + 'px'
25260 cls: 'roo-password-meter-text'
25266 if (this.hideTrigger) {
25267 this.trigger.setDisplayed(false);
25269 this.setSize(this.width || '', this.height || '');
25272 onDestroy: function ()
25274 if (this.trigger) {
25275 this.trigger.removeAllListeners();
25276 this.trigger.remove();
25279 this.wrap.remove();
25281 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25284 checkStrength: function ()
25286 var pwd = this.inputEl().getValue();
25287 if (pwd == this._lastPwd) {
25292 if (this.ClientSideStrongPassword(pwd)) {
25294 } else if (this.ClientSideMediumPassword(pwd)) {
25296 } else if (this.ClientSideWeakPassword(pwd)) {
25302 Roo.log('strength1: ' + strength);
25304 //var pm = this.trigger.child('div/div/div').dom;
25305 var pm = this.trigger.child('div/div');
25306 pm.removeClass(this.meterClass);
25307 pm.addClass(this.meterClass[strength]);
25310 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25312 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25314 this._lastPwd = pwd;
25318 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25320 this._lastPwd = '';
25322 var pm = this.trigger.child('div/div');
25323 pm.removeClass(this.meterClass);
25324 pm.addClass('roo-password-meter-grey');
25327 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25330 this.inputEl().dom.type='password';
25333 validateValue: function (value)
25335 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25338 if (value.length == 0) {
25339 if (this.allowBlank) {
25340 this.clearInvalid();
25344 this.markInvalid(this.errors.PwdEmpty);
25345 this.errorMsg = this.errors.PwdEmpty;
25353 if (!value.match(/[\x21-\x7e]+/)) {
25354 this.markInvalid(this.errors.PwdBadChar);
25355 this.errorMsg = this.errors.PwdBadChar;
25358 if (value.length < 6) {
25359 this.markInvalid(this.errors.PwdShort);
25360 this.errorMsg = this.errors.PwdShort;
25363 if (value.length > 16) {
25364 this.markInvalid(this.errors.PwdLong);
25365 this.errorMsg = this.errors.PwdLong;
25369 if (this.ClientSideStrongPassword(value)) {
25371 } else if (this.ClientSideMediumPassword(value)) {
25373 } else if (this.ClientSideWeakPassword(value)) {
25380 if (strength < 2) {
25381 //this.markInvalid(this.errors.TooWeak);
25382 this.errorMsg = this.errors.TooWeak;
25387 console.log('strength2: ' + strength);
25389 //var pm = this.trigger.child('div/div/div').dom;
25391 var pm = this.trigger.child('div/div');
25392 pm.removeClass(this.meterClass);
25393 pm.addClass(this.meterClass[strength]);
25395 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25397 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25399 this.errorMsg = '';
25403 CharacterSetChecks: function (type)
25406 this.fResult = false;
25409 isctype: function (character, type)
25412 case this.kCapitalLetter:
25413 if (character >= 'A' && character <= 'Z') {
25418 case this.kSmallLetter:
25419 if (character >= 'a' && character <= 'z') {
25425 if (character >= '0' && character <= '9') {
25430 case this.kPunctuation:
25431 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25442 IsLongEnough: function (pwd, size)
25444 return !(pwd == null || isNaN(size) || pwd.length < size);
25447 SpansEnoughCharacterSets: function (word, nb)
25449 if (!this.IsLongEnough(word, nb))
25454 var characterSetChecks = new Array(
25455 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25456 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25459 for (var index = 0; index < word.length; ++index) {
25460 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25461 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25462 characterSetChecks[nCharSet].fResult = true;
25469 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25470 if (characterSetChecks[nCharSet].fResult) {
25475 if (nCharSets < nb) {
25481 ClientSideStrongPassword: function (pwd)
25483 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25486 ClientSideMediumPassword: function (pwd)
25488 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25491 ClientSideWeakPassword: function (pwd)
25493 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25496 })//<script type="text/javascript">
25499 * Based Ext JS Library 1.1.1
25500 * Copyright(c) 2006-2007, Ext JS, LLC.
25506 * @class Roo.HtmlEditorCore
25507 * @extends Roo.Component
25508 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25510 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25513 Roo.HtmlEditorCore = function(config){
25516 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25521 * @event initialize
25522 * Fires when the editor is fully initialized (including the iframe)
25523 * @param {Roo.HtmlEditorCore} this
25528 * Fires when the editor is first receives the focus. Any insertion must wait
25529 * until after this event.
25530 * @param {Roo.HtmlEditorCore} this
25534 * @event beforesync
25535 * Fires before the textarea is updated with content from the editor iframe. Return false
25536 * to cancel the sync.
25537 * @param {Roo.HtmlEditorCore} this
25538 * @param {String} html
25542 * @event beforepush
25543 * Fires before the iframe editor is updated with content from the textarea. Return false
25544 * to cancel the push.
25545 * @param {Roo.HtmlEditorCore} this
25546 * @param {String} html
25551 * Fires when the textarea is updated with content from the editor iframe.
25552 * @param {Roo.HtmlEditorCore} this
25553 * @param {String} html
25558 * Fires when the iframe editor is updated with content from the textarea.
25559 * @param {Roo.HtmlEditorCore} this
25560 * @param {String} html
25565 * @event editorevent
25566 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25567 * @param {Roo.HtmlEditorCore} this
25573 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25575 // defaults : white / black...
25576 this.applyBlacklists();
25583 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25587 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25593 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25598 * @cfg {Number} height (in pixels)
25602 * @cfg {Number} width (in pixels)
25607 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25610 stylesheets: false,
25613 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25615 allowComments: false,
25619 // private properties
25620 validationEvent : false,
25622 initialized : false,
25624 sourceEditMode : false,
25625 onFocus : Roo.emptyFn,
25627 hideMode:'offsets',
25631 // blacklist + whitelisted elements..
25638 * Protected method that will not generally be called directly. It
25639 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25640 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25642 getDocMarkup : function(){
25646 // inherit styels from page...??
25647 if (this.stylesheets === false) {
25649 Roo.get(document.head).select('style').each(function(node) {
25650 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25653 Roo.get(document.head).select('link').each(function(node) {
25654 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25657 } else if (!this.stylesheets.length) {
25659 st = '<style type="text/css">' +
25660 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25663 for (var i in this.stylesheets) {
25664 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25669 st += '<style type="text/css">' +
25670 'IMG { cursor: pointer } ' +
25673 var cls = 'roo-htmleditor-body';
25675 if(this.bodyCls.length){
25676 cls += ' ' + this.bodyCls;
25679 return '<html><head>' + st +
25680 //<style type="text/css">' +
25681 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25683 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25687 onRender : function(ct, position)
25690 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25691 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25694 this.el.dom.style.border = '0 none';
25695 this.el.dom.setAttribute('tabIndex', -1);
25696 this.el.addClass('x-hidden hide');
25700 if(Roo.isIE){ // fix IE 1px bogus margin
25701 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25705 this.frameId = Roo.id();
25709 var iframe = this.owner.wrap.createChild({
25711 cls: 'form-control', // bootstrap..
25713 name: this.frameId,
25714 frameBorder : 'no',
25715 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25720 this.iframe = iframe.dom;
25722 this.assignDocWin();
25724 this.doc.designMode = 'on';
25727 this.doc.write(this.getDocMarkup());
25731 var task = { // must defer to wait for browser to be ready
25733 //console.log("run task?" + this.doc.readyState);
25734 this.assignDocWin();
25735 if(this.doc.body || this.doc.readyState == 'complete'){
25737 this.doc.designMode="on";
25741 Roo.TaskMgr.stop(task);
25742 this.initEditor.defer(10, this);
25749 Roo.TaskMgr.start(task);
25754 onResize : function(w, h)
25756 Roo.log('resize: ' +w + ',' + h );
25757 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25761 if(typeof w == 'number'){
25763 this.iframe.style.width = w + 'px';
25765 if(typeof h == 'number'){
25767 this.iframe.style.height = h + 'px';
25769 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25776 * Toggles the editor between standard and source edit mode.
25777 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25779 toggleSourceEdit : function(sourceEditMode){
25781 this.sourceEditMode = sourceEditMode === true;
25783 if(this.sourceEditMode){
25785 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25788 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25789 //this.iframe.className = '';
25792 //this.setSize(this.owner.wrap.getSize());
25793 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25800 * Protected method that will not generally be called directly. If you need/want
25801 * custom HTML cleanup, this is the method you should override.
25802 * @param {String} html The HTML to be cleaned
25803 * return {String} The cleaned HTML
25805 cleanHtml : function(html){
25806 html = String(html);
25807 if(html.length > 5){
25808 if(Roo.isSafari){ // strip safari nonsense
25809 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25812 if(html == ' '){
25819 * HTML Editor -> Textarea
25820 * Protected method that will not generally be called directly. Syncs the contents
25821 * of the editor iframe with the textarea.
25823 syncValue : function(){
25824 if(this.initialized){
25825 var bd = (this.doc.body || this.doc.documentElement);
25826 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25827 var html = bd.innerHTML;
25829 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25830 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25832 html = '<div style="'+m[0]+'">' + html + '</div>';
25835 html = this.cleanHtml(html);
25836 // fix up the special chars.. normaly like back quotes in word...
25837 // however we do not want to do this with chinese..
25838 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25840 var cc = match.charCodeAt();
25842 // Get the character value, handling surrogate pairs
25843 if (match.length == 2) {
25844 // It's a surrogate pair, calculate the Unicode code point
25845 var high = match.charCodeAt(0) - 0xD800;
25846 var low = match.charCodeAt(1) - 0xDC00;
25847 cc = (high * 0x400) + low + 0x10000;
25849 (cc >= 0x4E00 && cc < 0xA000 ) ||
25850 (cc >= 0x3400 && cc < 0x4E00 ) ||
25851 (cc >= 0xf900 && cc < 0xfb00 )
25856 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25857 return "&#" + cc + ";";
25864 if(this.owner.fireEvent('beforesync', this, html) !== false){
25865 this.el.dom.value = html;
25866 this.owner.fireEvent('sync', this, html);
25872 * Protected method that will not generally be called directly. Pushes the value of the textarea
25873 * into the iframe editor.
25875 pushValue : function(){
25876 if(this.initialized){
25877 var v = this.el.dom.value.trim();
25879 // if(v.length < 1){
25883 if(this.owner.fireEvent('beforepush', this, v) !== false){
25884 var d = (this.doc.body || this.doc.documentElement);
25886 this.cleanUpPaste();
25887 this.el.dom.value = d.innerHTML;
25888 this.owner.fireEvent('push', this, v);
25894 deferFocus : function(){
25895 this.focus.defer(10, this);
25899 focus : function(){
25900 if(this.win && !this.sourceEditMode){
25907 assignDocWin: function()
25909 var iframe = this.iframe;
25912 this.doc = iframe.contentWindow.document;
25913 this.win = iframe.contentWindow;
25915 // if (!Roo.get(this.frameId)) {
25918 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25919 // this.win = Roo.get(this.frameId).dom.contentWindow;
25921 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25925 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25926 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25931 initEditor : function(){
25932 //console.log("INIT EDITOR");
25933 this.assignDocWin();
25937 this.doc.designMode="on";
25939 this.doc.write(this.getDocMarkup());
25942 var dbody = (this.doc.body || this.doc.documentElement);
25943 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25944 // this copies styles from the containing element into thsi one..
25945 // not sure why we need all of this..
25946 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25948 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25949 //ss['background-attachment'] = 'fixed'; // w3c
25950 dbody.bgProperties = 'fixed'; // ie
25951 //Roo.DomHelper.applyStyles(dbody, ss);
25952 Roo.EventManager.on(this.doc, {
25953 //'mousedown': this.onEditorEvent,
25954 'mouseup': this.onEditorEvent,
25955 'dblclick': this.onEditorEvent,
25956 'click': this.onEditorEvent,
25957 'keyup': this.onEditorEvent,
25962 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25964 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25965 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25967 this.initialized = true;
25969 this.owner.fireEvent('initialize', this);
25974 onDestroy : function(){
25980 //for (var i =0; i < this.toolbars.length;i++) {
25981 // // fixme - ask toolbars for heights?
25982 // this.toolbars[i].onDestroy();
25985 //this.wrap.dom.innerHTML = '';
25986 //this.wrap.remove();
25991 onFirstFocus : function(){
25993 this.assignDocWin();
25996 this.activated = true;
25999 if(Roo.isGecko){ // prevent silly gecko errors
26001 var s = this.win.getSelection();
26002 if(!s.focusNode || s.focusNode.nodeType != 3){
26003 var r = s.getRangeAt(0);
26004 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26009 this.execCmd('useCSS', true);
26010 this.execCmd('styleWithCSS', false);
26013 this.owner.fireEvent('activate', this);
26017 adjustFont: function(btn){
26018 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26019 //if(Roo.isSafari){ // safari
26022 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26023 if(Roo.isSafari){ // safari
26024 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26025 v = (v < 10) ? 10 : v;
26026 v = (v > 48) ? 48 : v;
26027 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26032 v = Math.max(1, v+adjust);
26034 this.execCmd('FontSize', v );
26037 onEditorEvent : function(e)
26039 this.owner.fireEvent('editorevent', this, e);
26040 // this.updateToolbar();
26041 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26044 insertTag : function(tg)
26046 // could be a bit smarter... -> wrap the current selected tRoo..
26047 if (tg.toLowerCase() == 'span' ||
26048 tg.toLowerCase() == 'code' ||
26049 tg.toLowerCase() == 'sup' ||
26050 tg.toLowerCase() == 'sub'
26053 range = this.createRange(this.getSelection());
26054 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26055 wrappingNode.appendChild(range.extractContents());
26056 range.insertNode(wrappingNode);
26063 this.execCmd("formatblock", tg);
26067 insertText : function(txt)
26071 var range = this.createRange();
26072 range.deleteContents();
26073 //alert(Sender.getAttribute('label'));
26075 range.insertNode(this.doc.createTextNode(txt));
26081 * Executes a Midas editor command on the editor document and performs necessary focus and
26082 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26083 * @param {String} cmd The Midas command
26084 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26086 relayCmd : function(cmd, value){
26088 this.execCmd(cmd, value);
26089 this.owner.fireEvent('editorevent', this);
26090 //this.updateToolbar();
26091 this.owner.deferFocus();
26095 * Executes a Midas editor command directly on the editor document.
26096 * For visual commands, you should use {@link #relayCmd} instead.
26097 * <b>This should only be called after the editor is initialized.</b>
26098 * @param {String} cmd The Midas command
26099 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26101 execCmd : function(cmd, value){
26102 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26109 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26111 * @param {String} text | dom node..
26113 insertAtCursor : function(text)
26116 if(!this.activated){
26122 var r = this.doc.selection.createRange();
26133 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26137 // from jquery ui (MIT licenced)
26139 var win = this.win;
26141 if (win.getSelection && win.getSelection().getRangeAt) {
26142 range = win.getSelection().getRangeAt(0);
26143 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26144 range.insertNode(node);
26145 } else if (win.document.selection && win.document.selection.createRange) {
26146 // no firefox support
26147 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26148 win.document.selection.createRange().pasteHTML(txt);
26150 // no firefox support
26151 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26152 this.execCmd('InsertHTML', txt);
26161 mozKeyPress : function(e){
26163 var c = e.getCharCode(), cmd;
26166 c = String.fromCharCode(c).toLowerCase();
26180 this.cleanUpPaste.defer(100, this);
26188 e.preventDefault();
26196 fixKeys : function(){ // load time branching for fastest keydown performance
26198 return function(e){
26199 var k = e.getKey(), r;
26202 r = this.doc.selection.createRange();
26205 r.pasteHTML('    ');
26212 r = this.doc.selection.createRange();
26214 var target = r.parentElement();
26215 if(!target || target.tagName.toLowerCase() != 'li'){
26217 r.pasteHTML('<br />');
26223 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26224 this.cleanUpPaste.defer(100, this);
26230 }else if(Roo.isOpera){
26231 return function(e){
26232 var k = e.getKey();
26236 this.execCmd('InsertHTML','    ');
26239 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26240 this.cleanUpPaste.defer(100, this);
26245 }else if(Roo.isSafari){
26246 return function(e){
26247 var k = e.getKey();
26251 this.execCmd('InsertText','\t');
26255 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26256 this.cleanUpPaste.defer(100, this);
26264 getAllAncestors: function()
26266 var p = this.getSelectedNode();
26269 a.push(p); // push blank onto stack..
26270 p = this.getParentElement();
26274 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26278 a.push(this.doc.body);
26282 lastSelNode : false,
26285 getSelection : function()
26287 this.assignDocWin();
26288 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26291 getSelectedNode: function()
26293 // this may only work on Gecko!!!
26295 // should we cache this!!!!
26300 var range = this.createRange(this.getSelection()).cloneRange();
26303 var parent = range.parentElement();
26305 var testRange = range.duplicate();
26306 testRange.moveToElementText(parent);
26307 if (testRange.inRange(range)) {
26310 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26313 parent = parent.parentElement;
26318 // is ancestor a text element.
26319 var ac = range.commonAncestorContainer;
26320 if (ac.nodeType == 3) {
26321 ac = ac.parentNode;
26324 var ar = ac.childNodes;
26327 var other_nodes = [];
26328 var has_other_nodes = false;
26329 for (var i=0;i<ar.length;i++) {
26330 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26333 // fullly contained node.
26335 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26340 // probably selected..
26341 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26342 other_nodes.push(ar[i]);
26346 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26351 has_other_nodes = true;
26353 if (!nodes.length && other_nodes.length) {
26354 nodes= other_nodes;
26356 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26362 createRange: function(sel)
26364 // this has strange effects when using with
26365 // top toolbar - not sure if it's a great idea.
26366 //this.editor.contentWindow.focus();
26367 if (typeof sel != "undefined") {
26369 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26371 return this.doc.createRange();
26374 return this.doc.createRange();
26377 getParentElement: function()
26380 this.assignDocWin();
26381 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26383 var range = this.createRange(sel);
26386 var p = range.commonAncestorContainer;
26387 while (p.nodeType == 3) { // text node
26398 * Range intersection.. the hard stuff...
26402 * [ -- selected range --- ]
26406 * if end is before start or hits it. fail.
26407 * if start is after end or hits it fail.
26409 * if either hits (but other is outside. - then it's not
26415 // @see http://www.thismuchiknow.co.uk/?p=64.
26416 rangeIntersectsNode : function(range, node)
26418 var nodeRange = node.ownerDocument.createRange();
26420 nodeRange.selectNode(node);
26422 nodeRange.selectNodeContents(node);
26425 var rangeStartRange = range.cloneRange();
26426 rangeStartRange.collapse(true);
26428 var rangeEndRange = range.cloneRange();
26429 rangeEndRange.collapse(false);
26431 var nodeStartRange = nodeRange.cloneRange();
26432 nodeStartRange.collapse(true);
26434 var nodeEndRange = nodeRange.cloneRange();
26435 nodeEndRange.collapse(false);
26437 return rangeStartRange.compareBoundaryPoints(
26438 Range.START_TO_START, nodeEndRange) == -1 &&
26439 rangeEndRange.compareBoundaryPoints(
26440 Range.START_TO_START, nodeStartRange) == 1;
26444 rangeCompareNode : function(range, node)
26446 var nodeRange = node.ownerDocument.createRange();
26448 nodeRange.selectNode(node);
26450 nodeRange.selectNodeContents(node);
26454 range.collapse(true);
26456 nodeRange.collapse(true);
26458 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26459 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26461 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26463 var nodeIsBefore = ss == 1;
26464 var nodeIsAfter = ee == -1;
26466 if (nodeIsBefore && nodeIsAfter) {
26469 if (!nodeIsBefore && nodeIsAfter) {
26470 return 1; //right trailed.
26473 if (nodeIsBefore && !nodeIsAfter) {
26474 return 2; // left trailed.
26480 // private? - in a new class?
26481 cleanUpPaste : function()
26483 // cleans up the whole document..
26484 Roo.log('cleanuppaste');
26486 this.cleanUpChildren(this.doc.body);
26487 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26488 if (clean != this.doc.body.innerHTML) {
26489 this.doc.body.innerHTML = clean;
26494 cleanWordChars : function(input) {// change the chars to hex code
26495 var he = Roo.HtmlEditorCore;
26497 var output = input;
26498 Roo.each(he.swapCodes, function(sw) {
26499 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26501 output = output.replace(swapper, sw[1]);
26508 cleanUpChildren : function (n)
26510 if (!n.childNodes.length) {
26513 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26514 this.cleanUpChild(n.childNodes[i]);
26521 cleanUpChild : function (node)
26524 //console.log(node);
26525 if (node.nodeName == "#text") {
26526 // clean up silly Windows -- stuff?
26529 if (node.nodeName == "#comment") {
26530 if (!this.allowComments) {
26531 node.parentNode.removeChild(node);
26533 // clean up silly Windows -- stuff?
26536 var lcname = node.tagName.toLowerCase();
26537 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26538 // whitelist of tags..
26540 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26542 node.parentNode.removeChild(node);
26547 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26549 // spans with no attributes - just remove them..
26550 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26551 remove_keep_children = true;
26554 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26555 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26557 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26558 // remove_keep_children = true;
26561 if (remove_keep_children) {
26562 this.cleanUpChildren(node);
26563 // inserts everything just before this node...
26564 while (node.childNodes.length) {
26565 var cn = node.childNodes[0];
26566 node.removeChild(cn);
26567 node.parentNode.insertBefore(cn, node);
26569 node.parentNode.removeChild(node);
26573 if (!node.attributes || !node.attributes.length) {
26578 this.cleanUpChildren(node);
26582 function cleanAttr(n,v)
26585 if (v.match(/^\./) || v.match(/^\//)) {
26588 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26591 if (v.match(/^#/)) {
26594 if (v.match(/^\{/)) { // allow template editing.
26597 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26598 node.removeAttribute(n);
26602 var cwhite = this.cwhite;
26603 var cblack = this.cblack;
26605 function cleanStyle(n,v)
26607 if (v.match(/expression/)) { //XSS?? should we even bother..
26608 node.removeAttribute(n);
26612 var parts = v.split(/;/);
26615 Roo.each(parts, function(p) {
26616 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26620 var l = p.split(':').shift().replace(/\s+/g,'');
26621 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26623 if ( cwhite.length && cblack.indexOf(l) > -1) {
26624 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26625 //node.removeAttribute(n);
26629 // only allow 'c whitelisted system attributes'
26630 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26631 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26632 //node.removeAttribute(n);
26642 if (clean.length) {
26643 node.setAttribute(n, clean.join(';'));
26645 node.removeAttribute(n);
26651 for (var i = node.attributes.length-1; i > -1 ; i--) {
26652 var a = node.attributes[i];
26655 if (a.name.toLowerCase().substr(0,2)=='on') {
26656 node.removeAttribute(a.name);
26659 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26660 node.removeAttribute(a.name);
26663 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26664 cleanAttr(a.name,a.value); // fixme..
26667 if (a.name == 'style') {
26668 cleanStyle(a.name,a.value);
26671 /// clean up MS crap..
26672 // tecnically this should be a list of valid class'es..
26675 if (a.name == 'class') {
26676 if (a.value.match(/^Mso/)) {
26677 node.removeAttribute('class');
26680 if (a.value.match(/^body$/)) {
26681 node.removeAttribute('class');
26692 this.cleanUpChildren(node);
26698 * Clean up MS wordisms...
26700 cleanWord : function(node)
26703 this.cleanWord(this.doc.body);
26708 node.nodeName == 'SPAN' &&
26709 !node.hasAttributes() &&
26710 node.childNodes.length == 1 &&
26711 node.firstChild.nodeName == "#text"
26713 var textNode = node.firstChild;
26714 node.removeChild(textNode);
26715 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26716 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26718 node.parentNode.insertBefore(textNode, node);
26719 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26720 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26722 node.parentNode.removeChild(node);
26725 if (node.nodeName == "#text") {
26726 // clean up silly Windows -- stuff?
26729 if (node.nodeName == "#comment") {
26730 node.parentNode.removeChild(node);
26731 // clean up silly Windows -- stuff?
26735 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26736 node.parentNode.removeChild(node);
26739 //Roo.log(node.tagName);
26740 // remove - but keep children..
26741 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26742 //Roo.log('-- removed');
26743 while (node.childNodes.length) {
26744 var cn = node.childNodes[0];
26745 node.removeChild(cn);
26746 node.parentNode.insertBefore(cn, node);
26747 // move node to parent - and clean it..
26748 this.cleanWord(cn);
26750 node.parentNode.removeChild(node);
26751 /// no need to iterate chidlren = it's got none..
26752 //this.iterateChildren(node, this.cleanWord);
26756 if (node.className.length) {
26758 var cn = node.className.split(/\W+/);
26760 Roo.each(cn, function(cls) {
26761 if (cls.match(/Mso[a-zA-Z]+/)) {
26766 node.className = cna.length ? cna.join(' ') : '';
26768 node.removeAttribute("class");
26772 if (node.hasAttribute("lang")) {
26773 node.removeAttribute("lang");
26776 if (node.hasAttribute("style")) {
26778 var styles = node.getAttribute("style").split(";");
26780 Roo.each(styles, function(s) {
26781 if (!s.match(/:/)) {
26784 var kv = s.split(":");
26785 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26788 // what ever is left... we allow.
26791 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26792 if (!nstyle.length) {
26793 node.removeAttribute('style');
26796 this.iterateChildren(node, this.cleanWord);
26802 * iterateChildren of a Node, calling fn each time, using this as the scole..
26803 * @param {DomNode} node node to iterate children of.
26804 * @param {Function} fn method of this class to call on each item.
26806 iterateChildren : function(node, fn)
26808 if (!node.childNodes.length) {
26811 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26812 fn.call(this, node.childNodes[i])
26818 * cleanTableWidths.
26820 * Quite often pasting from word etc.. results in tables with column and widths.
26821 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26824 cleanTableWidths : function(node)
26829 this.cleanTableWidths(this.doc.body);
26834 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26837 Roo.log(node.tagName);
26838 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26839 this.iterateChildren(node, this.cleanTableWidths);
26842 if (node.hasAttribute('width')) {
26843 node.removeAttribute('width');
26847 if (node.hasAttribute("style")) {
26850 var styles = node.getAttribute("style").split(";");
26852 Roo.each(styles, function(s) {
26853 if (!s.match(/:/)) {
26856 var kv = s.split(":");
26857 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26860 // what ever is left... we allow.
26863 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26864 if (!nstyle.length) {
26865 node.removeAttribute('style');
26869 this.iterateChildren(node, this.cleanTableWidths);
26877 domToHTML : function(currentElement, depth, nopadtext) {
26879 depth = depth || 0;
26880 nopadtext = nopadtext || false;
26882 if (!currentElement) {
26883 return this.domToHTML(this.doc.body);
26886 //Roo.log(currentElement);
26888 var allText = false;
26889 var nodeName = currentElement.nodeName;
26890 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26892 if (nodeName == '#text') {
26894 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26899 if (nodeName != 'BODY') {
26902 // Prints the node tagName, such as <A>, <IMG>, etc
26905 for(i = 0; i < currentElement.attributes.length;i++) {
26907 var aname = currentElement.attributes.item(i).name;
26908 if (!currentElement.attributes.item(i).value.length) {
26911 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26914 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26923 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26926 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26931 // Traverse the tree
26933 var currentElementChild = currentElement.childNodes.item(i);
26934 var allText = true;
26935 var innerHTML = '';
26937 while (currentElementChild) {
26938 // Formatting code (indent the tree so it looks nice on the screen)
26939 var nopad = nopadtext;
26940 if (lastnode == 'SPAN') {
26944 if (currentElementChild.nodeName == '#text') {
26945 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26946 toadd = nopadtext ? toadd : toadd.trim();
26947 if (!nopad && toadd.length > 80) {
26948 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26950 innerHTML += toadd;
26953 currentElementChild = currentElement.childNodes.item(i);
26959 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26961 // Recursively traverse the tree structure of the child node
26962 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26963 lastnode = currentElementChild.nodeName;
26965 currentElementChild=currentElement.childNodes.item(i);
26971 // The remaining code is mostly for formatting the tree
26972 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26977 ret+= "</"+tagName+">";
26983 applyBlacklists : function()
26985 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26986 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26990 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26991 if (b.indexOf(tag) > -1) {
26994 this.white.push(tag);
26998 Roo.each(w, function(tag) {
26999 if (b.indexOf(tag) > -1) {
27002 if (this.white.indexOf(tag) > -1) {
27005 this.white.push(tag);
27010 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27011 if (w.indexOf(tag) > -1) {
27014 this.black.push(tag);
27018 Roo.each(b, function(tag) {
27019 if (w.indexOf(tag) > -1) {
27022 if (this.black.indexOf(tag) > -1) {
27025 this.black.push(tag);
27030 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27031 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27035 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27036 if (b.indexOf(tag) > -1) {
27039 this.cwhite.push(tag);
27043 Roo.each(w, function(tag) {
27044 if (b.indexOf(tag) > -1) {
27047 if (this.cwhite.indexOf(tag) > -1) {
27050 this.cwhite.push(tag);
27055 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27056 if (w.indexOf(tag) > -1) {
27059 this.cblack.push(tag);
27063 Roo.each(b, function(tag) {
27064 if (w.indexOf(tag) > -1) {
27067 if (this.cblack.indexOf(tag) > -1) {
27070 this.cblack.push(tag);
27075 setStylesheets : function(stylesheets)
27077 if(typeof(stylesheets) == 'string'){
27078 Roo.get(this.iframe.contentDocument.head).createChild({
27080 rel : 'stylesheet',
27089 Roo.each(stylesheets, function(s) {
27094 Roo.get(_this.iframe.contentDocument.head).createChild({
27096 rel : 'stylesheet',
27105 removeStylesheets : function()
27109 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27114 setStyle : function(style)
27116 Roo.get(this.iframe.contentDocument.head).createChild({
27125 // hide stuff that is not compatible
27139 * @event specialkey
27143 * @cfg {String} fieldClass @hide
27146 * @cfg {String} focusClass @hide
27149 * @cfg {String} autoCreate @hide
27152 * @cfg {String} inputType @hide
27155 * @cfg {String} invalidClass @hide
27158 * @cfg {String} invalidText @hide
27161 * @cfg {String} msgFx @hide
27164 * @cfg {String} validateOnBlur @hide
27168 Roo.HtmlEditorCore.white = [
27169 'area', 'br', 'img', 'input', 'hr', 'wbr',
27171 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27172 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27173 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27174 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27175 'table', 'ul', 'xmp',
27177 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27180 'dir', 'menu', 'ol', 'ul', 'dl',
27186 Roo.HtmlEditorCore.black = [
27187 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27189 'base', 'basefont', 'bgsound', 'blink', 'body',
27190 'frame', 'frameset', 'head', 'html', 'ilayer',
27191 'iframe', 'layer', 'link', 'meta', 'object',
27192 'script', 'style' ,'title', 'xml' // clean later..
27194 Roo.HtmlEditorCore.clean = [
27195 'script', 'style', 'title', 'xml'
27197 Roo.HtmlEditorCore.remove = [
27202 Roo.HtmlEditorCore.ablack = [
27206 Roo.HtmlEditorCore.aclean = [
27207 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27211 Roo.HtmlEditorCore.pwhite= [
27212 'http', 'https', 'mailto'
27215 // white listed style attributes.
27216 Roo.HtmlEditorCore.cwhite= [
27217 // 'text-align', /// default is to allow most things..
27223 // black listed style attributes.
27224 Roo.HtmlEditorCore.cblack= [
27225 // 'font-size' -- this can be set by the project
27229 Roo.HtmlEditorCore.swapCodes =[
27230 [ 8211, "–" ],
27231 [ 8212, "—" ],
27248 * @class Roo.bootstrap.HtmlEditor
27249 * @extends Roo.bootstrap.TextArea
27250 * Bootstrap HtmlEditor class
27253 * Create a new HtmlEditor
27254 * @param {Object} config The config object
27257 Roo.bootstrap.HtmlEditor = function(config){
27258 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27259 if (!this.toolbars) {
27260 this.toolbars = [];
27263 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27266 * @event initialize
27267 * Fires when the editor is fully initialized (including the iframe)
27268 * @param {HtmlEditor} this
27273 * Fires when the editor is first receives the focus. Any insertion must wait
27274 * until after this event.
27275 * @param {HtmlEditor} this
27279 * @event beforesync
27280 * Fires before the textarea is updated with content from the editor iframe. Return false
27281 * to cancel the sync.
27282 * @param {HtmlEditor} this
27283 * @param {String} html
27287 * @event beforepush
27288 * Fires before the iframe editor is updated with content from the textarea. Return false
27289 * to cancel the push.
27290 * @param {HtmlEditor} this
27291 * @param {String} html
27296 * Fires when the textarea is updated with content from the editor iframe.
27297 * @param {HtmlEditor} this
27298 * @param {String} html
27303 * Fires when the iframe editor is updated with content from the textarea.
27304 * @param {HtmlEditor} this
27305 * @param {String} html
27309 * @event editmodechange
27310 * Fires when the editor switches edit modes
27311 * @param {HtmlEditor} this
27312 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27314 editmodechange: true,
27316 * @event editorevent
27317 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27318 * @param {HtmlEditor} this
27322 * @event firstfocus
27323 * Fires when on first focus - needed by toolbars..
27324 * @param {HtmlEditor} this
27329 * Auto save the htmlEditor value as a file into Events
27330 * @param {HtmlEditor} this
27334 * @event savedpreview
27335 * preview the saved version of htmlEditor
27336 * @param {HtmlEditor} this
27343 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27347 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27352 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27357 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27362 * @cfg {Number} height (in pixels)
27366 * @cfg {Number} width (in pixels)
27371 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27374 stylesheets: false,
27379 // private properties
27380 validationEvent : false,
27382 initialized : false,
27385 onFocus : Roo.emptyFn,
27387 hideMode:'offsets',
27389 tbContainer : false,
27393 toolbarContainer :function() {
27394 return this.wrap.select('.x-html-editor-tb',true).first();
27398 * Protected method that will not generally be called directly. It
27399 * is called when the editor creates its toolbar. Override this method if you need to
27400 * add custom toolbar buttons.
27401 * @param {HtmlEditor} editor
27403 createToolbar : function(){
27404 Roo.log('renewing');
27405 Roo.log("create toolbars");
27407 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27408 this.toolbars[0].render(this.toolbarContainer());
27412 // if (!editor.toolbars || !editor.toolbars.length) {
27413 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27416 // for (var i =0 ; i < editor.toolbars.length;i++) {
27417 // editor.toolbars[i] = Roo.factory(
27418 // typeof(editor.toolbars[i]) == 'string' ?
27419 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27420 // Roo.bootstrap.HtmlEditor);
27421 // editor.toolbars[i].init(editor);
27427 onRender : function(ct, position)
27429 // Roo.log("Call onRender: " + this.xtype);
27431 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27433 this.wrap = this.inputEl().wrap({
27434 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27437 this.editorcore.onRender(ct, position);
27439 if (this.resizable) {
27440 this.resizeEl = new Roo.Resizable(this.wrap, {
27444 minHeight : this.height,
27445 height: this.height,
27446 handles : this.resizable,
27449 resize : function(r, w, h) {
27450 _t.onResize(w,h); // -something
27456 this.createToolbar(this);
27459 if(!this.width && this.resizable){
27460 this.setSize(this.wrap.getSize());
27462 if (this.resizeEl) {
27463 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27464 // should trigger onReize..
27470 onResize : function(w, h)
27472 Roo.log('resize: ' +w + ',' + h );
27473 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27477 if(this.inputEl() ){
27478 if(typeof w == 'number'){
27479 var aw = w - this.wrap.getFrameWidth('lr');
27480 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27483 if(typeof h == 'number'){
27484 var tbh = -11; // fixme it needs to tool bar size!
27485 for (var i =0; i < this.toolbars.length;i++) {
27486 // fixme - ask toolbars for heights?
27487 tbh += this.toolbars[i].el.getHeight();
27488 //if (this.toolbars[i].footer) {
27489 // tbh += this.toolbars[i].footer.el.getHeight();
27497 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27498 ah -= 5; // knock a few pixes off for look..
27499 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27503 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27504 this.editorcore.onResize(ew,eh);
27509 * Toggles the editor between standard and source edit mode.
27510 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27512 toggleSourceEdit : function(sourceEditMode)
27514 this.editorcore.toggleSourceEdit(sourceEditMode);
27516 if(this.editorcore.sourceEditMode){
27517 Roo.log('editor - showing textarea');
27520 // Roo.log(this.syncValue());
27522 this.inputEl().removeClass(['hide', 'x-hidden']);
27523 this.inputEl().dom.removeAttribute('tabIndex');
27524 this.inputEl().focus();
27526 Roo.log('editor - hiding textarea');
27528 // Roo.log(this.pushValue());
27531 this.inputEl().addClass(['hide', 'x-hidden']);
27532 this.inputEl().dom.setAttribute('tabIndex', -1);
27533 //this.deferFocus();
27536 if(this.resizable){
27537 this.setSize(this.wrap.getSize());
27540 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27543 // private (for BoxComponent)
27544 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27546 // private (for BoxComponent)
27547 getResizeEl : function(){
27551 // private (for BoxComponent)
27552 getPositionEl : function(){
27557 initEvents : function(){
27558 this.originalValue = this.getValue();
27562 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27565 // markInvalid : Roo.emptyFn,
27567 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27570 // clearInvalid : Roo.emptyFn,
27572 setValue : function(v){
27573 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27574 this.editorcore.pushValue();
27579 deferFocus : function(){
27580 this.focus.defer(10, this);
27584 focus : function(){
27585 this.editorcore.focus();
27591 onDestroy : function(){
27597 for (var i =0; i < this.toolbars.length;i++) {
27598 // fixme - ask toolbars for heights?
27599 this.toolbars[i].onDestroy();
27602 this.wrap.dom.innerHTML = '';
27603 this.wrap.remove();
27608 onFirstFocus : function(){
27609 //Roo.log("onFirstFocus");
27610 this.editorcore.onFirstFocus();
27611 for (var i =0; i < this.toolbars.length;i++) {
27612 this.toolbars[i].onFirstFocus();
27618 syncValue : function()
27620 this.editorcore.syncValue();
27623 pushValue : function()
27625 this.editorcore.pushValue();
27629 // hide stuff that is not compatible
27643 * @event specialkey
27647 * @cfg {String} fieldClass @hide
27650 * @cfg {String} focusClass @hide
27653 * @cfg {String} autoCreate @hide
27656 * @cfg {String} inputType @hide
27660 * @cfg {String} invalidText @hide
27663 * @cfg {String} msgFx @hide
27666 * @cfg {String} validateOnBlur @hide
27675 Roo.namespace('Roo.bootstrap.htmleditor');
27677 * @class Roo.bootstrap.HtmlEditorToolbar1
27683 new Roo.bootstrap.HtmlEditor({
27686 new Roo.bootstrap.HtmlEditorToolbar1({
27687 disable : { fonts: 1 , format: 1, ..., ... , ...],
27693 * @cfg {Object} disable List of elements to disable..
27694 * @cfg {Array} btns List of additional buttons.
27698 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27701 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27704 Roo.apply(this, config);
27706 // default disabled, based on 'good practice'..
27707 this.disable = this.disable || {};
27708 Roo.applyIf(this.disable, {
27711 specialElements : true
27713 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27715 this.editor = config.editor;
27716 this.editorcore = config.editor.editorcore;
27718 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27720 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27721 // dont call parent... till later.
27723 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27728 editorcore : false,
27733 "h1","h2","h3","h4","h5","h6",
27735 "abbr", "acronym", "address", "cite", "samp", "var",
27739 onRender : function(ct, position)
27741 // Roo.log("Call onRender: " + this.xtype);
27743 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27745 this.el.dom.style.marginBottom = '0';
27747 var editorcore = this.editorcore;
27748 var editor= this.editor;
27751 var btn = function(id,cmd , toggle, handler, html){
27753 var event = toggle ? 'toggle' : 'click';
27758 xns: Roo.bootstrap,
27762 enableToggle:toggle !== false,
27764 pressed : toggle ? false : null,
27767 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27768 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27774 // var cb_box = function...
27779 xns: Roo.bootstrap,
27784 xns: Roo.bootstrap,
27788 Roo.each(this.formats, function(f) {
27789 style.menu.items.push({
27791 xns: Roo.bootstrap,
27792 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27797 editorcore.insertTag(this.tagname);
27804 children.push(style);
27806 btn('bold',false,true);
27807 btn('italic',false,true);
27808 btn('align-left', 'justifyleft',true);
27809 btn('align-center', 'justifycenter',true);
27810 btn('align-right' , 'justifyright',true);
27811 btn('link', false, false, function(btn) {
27812 //Roo.log("create link?");
27813 var url = prompt(this.createLinkText, this.defaultLinkValue);
27814 if(url && url != 'http:/'+'/'){
27815 this.editorcore.relayCmd('createlink', url);
27818 btn('list','insertunorderedlist',true);
27819 btn('pencil', false,true, function(btn){
27821 this.toggleSourceEdit(btn.pressed);
27824 if (this.editor.btns.length > 0) {
27825 for (var i = 0; i<this.editor.btns.length; i++) {
27826 children.push(this.editor.btns[i]);
27834 xns: Roo.bootstrap,
27839 xns: Roo.bootstrap,
27844 cog.menu.items.push({
27846 xns: Roo.bootstrap,
27847 html : Clean styles,
27852 editorcore.insertTag(this.tagname);
27861 this.xtype = 'NavSimplebar';
27863 for(var i=0;i< children.length;i++) {
27865 this.buttons.add(this.addxtypeChild(children[i]));
27869 editor.on('editorevent', this.updateToolbar, this);
27871 onBtnClick : function(id)
27873 this.editorcore.relayCmd(id);
27874 this.editorcore.focus();
27878 * Protected method that will not generally be called directly. It triggers
27879 * a toolbar update by reading the markup state of the current selection in the editor.
27881 updateToolbar: function(){
27883 if(!this.editorcore.activated){
27884 this.editor.onFirstFocus(); // is this neeed?
27888 var btns = this.buttons;
27889 var doc = this.editorcore.doc;
27890 btns.get('bold').setActive(doc.queryCommandState('bold'));
27891 btns.get('italic').setActive(doc.queryCommandState('italic'));
27892 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27894 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27895 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27896 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27898 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27899 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27902 var ans = this.editorcore.getAllAncestors();
27903 if (this.formatCombo) {
27906 var store = this.formatCombo.store;
27907 this.formatCombo.setValue("");
27908 for (var i =0; i < ans.length;i++) {
27909 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27911 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27919 // hides menus... - so this cant be on a menu...
27920 Roo.bootstrap.MenuMgr.hideAll();
27922 Roo.bootstrap.MenuMgr.hideAll();
27923 //this.editorsyncValue();
27925 onFirstFocus: function() {
27926 this.buttons.each(function(item){
27930 toggleSourceEdit : function(sourceEditMode){
27933 if(sourceEditMode){
27934 Roo.log("disabling buttons");
27935 this.buttons.each( function(item){
27936 if(item.cmd != 'pencil'){
27942 Roo.log("enabling buttons");
27943 if(this.editorcore.initialized){
27944 this.buttons.each( function(item){
27950 Roo.log("calling toggole on editor");
27951 // tell the editor that it's been pressed..
27952 this.editor.toggleSourceEdit(sourceEditMode);
27966 * @class Roo.bootstrap.Markdown
27967 * @extends Roo.bootstrap.TextArea
27968 * Bootstrap Showdown editable area
27969 * @cfg {string} content
27972 * Create a new Showdown
27975 Roo.bootstrap.Markdown = function(config){
27976 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27980 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27984 initEvents : function()
27987 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27988 this.markdownEl = this.el.createChild({
27989 cls : 'roo-markdown-area'
27991 this.inputEl().addClass('d-none');
27992 if (this.getValue() == '') {
27993 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27996 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27998 this.markdownEl.on('click', this.toggleTextEdit, this);
27999 this.on('blur', this.toggleTextEdit, this);
28000 this.on('specialkey', this.resizeTextArea, this);
28003 toggleTextEdit : function()
28005 var sh = this.markdownEl.getHeight();
28006 this.inputEl().addClass('d-none');
28007 this.markdownEl.addClass('d-none');
28008 if (!this.editing) {
28010 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28011 this.inputEl().removeClass('d-none');
28012 this.inputEl().focus();
28013 this.editing = true;
28016 // show showdown...
28017 this.updateMarkdown();
28018 this.markdownEl.removeClass('d-none');
28019 this.editing = false;
28022 updateMarkdown : function()
28024 if (this.getValue() == '') {
28025 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28029 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28032 resizeTextArea: function () {
28035 Roo.log([sh, this.getValue().split("\n").length * 30]);
28036 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28038 setValue : function(val)
28040 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28041 if (!this.editing) {
28042 this.updateMarkdown();
28048 if (!this.editing) {
28049 this.toggleTextEdit();
28057 * Ext JS Library 1.1.1
28058 * Copyright(c) 2006-2007, Ext JS, LLC.
28060 * Originally Released Under LGPL - original licence link has changed is not relivant.
28063 * <script type="text/javascript">
28067 * @class Roo.bootstrap.PagingToolbar
28068 * @extends Roo.bootstrap.NavSimplebar
28069 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28071 * Create a new PagingToolbar
28072 * @param {Object} config The config object
28073 * @param {Roo.data.Store} store
28075 Roo.bootstrap.PagingToolbar = function(config)
28077 // old args format still supported... - xtype is prefered..
28078 // created from xtype...
28080 this.ds = config.dataSource;
28082 if (config.store && !this.ds) {
28083 this.store= Roo.factory(config.store, Roo.data);
28084 this.ds = this.store;
28085 this.ds.xmodule = this.xmodule || false;
28088 this.toolbarItems = [];
28089 if (config.items) {
28090 this.toolbarItems = config.items;
28093 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28098 this.bind(this.ds);
28101 if (Roo.bootstrap.version == 4) {
28102 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28104 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28109 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28111 * @cfg {Roo.data.Store} dataSource
28112 * The underlying data store providing the paged data
28115 * @cfg {String/HTMLElement/Element} container
28116 * container The id or element that will contain the toolbar
28119 * @cfg {Boolean} displayInfo
28120 * True to display the displayMsg (defaults to false)
28123 * @cfg {Number} pageSize
28124 * The number of records to display per page (defaults to 20)
28128 * @cfg {String} displayMsg
28129 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28131 displayMsg : 'Displaying {0} - {1} of {2}',
28133 * @cfg {String} emptyMsg
28134 * The message to display when no records are found (defaults to "No data to display")
28136 emptyMsg : 'No data to display',
28138 * Customizable piece of the default paging text (defaults to "Page")
28141 beforePageText : "Page",
28143 * Customizable piece of the default paging text (defaults to "of %0")
28146 afterPageText : "of {0}",
28148 * Customizable piece of the default paging text (defaults to "First Page")
28151 firstText : "First Page",
28153 * Customizable piece of the default paging text (defaults to "Previous Page")
28156 prevText : "Previous Page",
28158 * Customizable piece of the default paging text (defaults to "Next Page")
28161 nextText : "Next Page",
28163 * Customizable piece of the default paging text (defaults to "Last Page")
28166 lastText : "Last Page",
28168 * Customizable piece of the default paging text (defaults to "Refresh")
28171 refreshText : "Refresh",
28175 onRender : function(ct, position)
28177 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28178 this.navgroup.parentId = this.id;
28179 this.navgroup.onRender(this.el, null);
28180 // add the buttons to the navgroup
28182 if(this.displayInfo){
28183 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28184 this.displayEl = this.el.select('.x-paging-info', true).first();
28185 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28186 // this.displayEl = navel.el.select('span',true).first();
28192 Roo.each(_this.buttons, function(e){ // this might need to use render????
28193 Roo.factory(e).render(_this.el);
28197 Roo.each(_this.toolbarItems, function(e) {
28198 _this.navgroup.addItem(e);
28202 this.first = this.navgroup.addItem({
28203 tooltip: this.firstText,
28204 cls: "prev btn-outline-secondary",
28205 html : ' <i class="fa fa-step-backward"></i>',
28207 preventDefault: true,
28208 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28211 this.prev = this.navgroup.addItem({
28212 tooltip: this.prevText,
28213 cls: "prev btn-outline-secondary",
28214 html : ' <i class="fa fa-backward"></i>',
28216 preventDefault: true,
28217 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28219 //this.addSeparator();
28222 var field = this.navgroup.addItem( {
28224 cls : 'x-paging-position btn-outline-secondary',
28226 html : this.beforePageText +
28227 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28228 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28231 this.field = field.el.select('input', true).first();
28232 this.field.on("keydown", this.onPagingKeydown, this);
28233 this.field.on("focus", function(){this.dom.select();});
28236 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28237 //this.field.setHeight(18);
28238 //this.addSeparator();
28239 this.next = this.navgroup.addItem({
28240 tooltip: this.nextText,
28241 cls: "next btn-outline-secondary",
28242 html : ' <i class="fa fa-forward"></i>',
28244 preventDefault: true,
28245 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28247 this.last = this.navgroup.addItem({
28248 tooltip: this.lastText,
28249 html : ' <i class="fa fa-step-forward"></i>',
28250 cls: "next btn-outline-secondary",
28252 preventDefault: true,
28253 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28255 //this.addSeparator();
28256 this.loading = this.navgroup.addItem({
28257 tooltip: this.refreshText,
28258 cls: "btn-outline-secondary",
28259 html : ' <i class="fa fa-refresh"></i>',
28260 preventDefault: true,
28261 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28267 updateInfo : function(){
28268 if(this.displayEl){
28269 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28270 var msg = count == 0 ?
28274 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28276 this.displayEl.update(msg);
28281 onLoad : function(ds, r, o)
28283 this.cursor = o.params && o.params.start ? o.params.start : 0;
28285 var d = this.getPageData(),
28290 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28291 this.field.dom.value = ap;
28292 this.first.setDisabled(ap == 1);
28293 this.prev.setDisabled(ap == 1);
28294 this.next.setDisabled(ap == ps);
28295 this.last.setDisabled(ap == ps);
28296 this.loading.enable();
28301 getPageData : function(){
28302 var total = this.ds.getTotalCount();
28305 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28306 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28311 onLoadError : function(){
28312 this.loading.enable();
28316 onPagingKeydown : function(e){
28317 var k = e.getKey();
28318 var d = this.getPageData();
28320 var v = this.field.dom.value, pageNum;
28321 if(!v || isNaN(pageNum = parseInt(v, 10))){
28322 this.field.dom.value = d.activePage;
28325 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28326 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28329 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))
28331 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28332 this.field.dom.value = pageNum;
28333 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28336 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28338 var v = this.field.dom.value, pageNum;
28339 var increment = (e.shiftKey) ? 10 : 1;
28340 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28343 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28344 this.field.dom.value = d.activePage;
28347 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28349 this.field.dom.value = parseInt(v, 10) + increment;
28350 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28351 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28358 beforeLoad : function(){
28360 this.loading.disable();
28365 onClick : function(which){
28374 ds.load({params:{start: 0, limit: this.pageSize}});
28377 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28380 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28383 var total = ds.getTotalCount();
28384 var extra = total % this.pageSize;
28385 var lastStart = extra ? (total - extra) : total-this.pageSize;
28386 ds.load({params:{start: lastStart, limit: this.pageSize}});
28389 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28395 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28396 * @param {Roo.data.Store} store The data store to unbind
28398 unbind : function(ds){
28399 ds.un("beforeload", this.beforeLoad, this);
28400 ds.un("load", this.onLoad, this);
28401 ds.un("loadexception", this.onLoadError, this);
28402 ds.un("remove", this.updateInfo, this);
28403 ds.un("add", this.updateInfo, this);
28404 this.ds = undefined;
28408 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28409 * @param {Roo.data.Store} store The data store to bind
28411 bind : function(ds){
28412 ds.on("beforeload", this.beforeLoad, this);
28413 ds.on("load", this.onLoad, this);
28414 ds.on("loadexception", this.onLoadError, this);
28415 ds.on("remove", this.updateInfo, this);
28416 ds.on("add", this.updateInfo, this);
28427 * @class Roo.bootstrap.MessageBar
28428 * @extends Roo.bootstrap.Component
28429 * Bootstrap MessageBar class
28430 * @cfg {String} html contents of the MessageBar
28431 * @cfg {String} weight (info | success | warning | danger) default info
28432 * @cfg {String} beforeClass insert the bar before the given class
28433 * @cfg {Boolean} closable (true | false) default false
28434 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28437 * Create a new Element
28438 * @param {Object} config The config object
28441 Roo.bootstrap.MessageBar = function(config){
28442 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28445 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28451 beforeClass: 'bootstrap-sticky-wrap',
28453 getAutoCreate : function(){
28457 cls: 'alert alert-dismissable alert-' + this.weight,
28462 html: this.html || ''
28468 cfg.cls += ' alert-messages-fixed';
28482 onRender : function(ct, position)
28484 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28487 var cfg = Roo.apply({}, this.getAutoCreate());
28491 cfg.cls += ' ' + this.cls;
28494 cfg.style = this.style;
28496 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28498 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28501 this.el.select('>button.close').on('click', this.hide, this);
28507 if (!this.rendered) {
28513 this.fireEvent('show', this);
28519 if (!this.rendered) {
28525 this.fireEvent('hide', this);
28528 update : function()
28530 // var e = this.el.dom.firstChild;
28532 // if(this.closable){
28533 // e = e.nextSibling;
28536 // e.data = this.html || '';
28538 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28554 * @class Roo.bootstrap.Graph
28555 * @extends Roo.bootstrap.Component
28556 * Bootstrap Graph class
28560 @cfg {String} graphtype bar | vbar | pie
28561 @cfg {number} g_x coodinator | centre x (pie)
28562 @cfg {number} g_y coodinator | centre y (pie)
28563 @cfg {number} g_r radius (pie)
28564 @cfg {number} g_height height of the chart (respected by all elements in the set)
28565 @cfg {number} g_width width of the chart (respected by all elements in the set)
28566 @cfg {Object} title The title of the chart
28569 -opts (object) options for the chart
28571 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28572 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28574 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.
28575 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28577 o stretch (boolean)
28579 -opts (object) options for the pie
28582 o startAngle (number)
28583 o endAngle (number)
28587 * Create a new Input
28588 * @param {Object} config The config object
28591 Roo.bootstrap.Graph = function(config){
28592 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28598 * The img click event for the img.
28599 * @param {Roo.EventObject} e
28605 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28616 //g_colors: this.colors,
28623 getAutoCreate : function(){
28634 onRender : function(ct,position){
28637 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28639 if (typeof(Raphael) == 'undefined') {
28640 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28644 this.raphael = Raphael(this.el.dom);
28646 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28647 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28648 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28649 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28651 r.text(160, 10, "Single Series Chart").attr(txtattr);
28652 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28653 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28654 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28656 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28657 r.barchart(330, 10, 300, 220, data1);
28658 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28659 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28662 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28663 // r.barchart(30, 30, 560, 250, xdata, {
28664 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28665 // axis : "0 0 1 1",
28666 // axisxlabels : xdata
28667 // //yvalues : cols,
28670 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28672 // this.load(null,xdata,{
28673 // axis : "0 0 1 1",
28674 // axisxlabels : xdata
28679 load : function(graphtype,xdata,opts)
28681 this.raphael.clear();
28683 graphtype = this.graphtype;
28688 var r = this.raphael,
28689 fin = function () {
28690 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28692 fout = function () {
28693 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28695 pfin = function() {
28696 this.sector.stop();
28697 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28700 this.label[0].stop();
28701 this.label[0].attr({ r: 7.5 });
28702 this.label[1].attr({ "font-weight": 800 });
28705 pfout = function() {
28706 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28709 this.label[0].animate({ r: 5 }, 500, "bounce");
28710 this.label[1].attr({ "font-weight": 400 });
28716 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28719 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28722 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28723 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28725 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28732 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28737 setTitle: function(o)
28742 initEvents: function() {
28745 this.el.on('click', this.onClick, this);
28749 onClick : function(e)
28751 Roo.log('img onclick');
28752 this.fireEvent('click', this, e);
28764 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28767 * @class Roo.bootstrap.dash.NumberBox
28768 * @extends Roo.bootstrap.Component
28769 * Bootstrap NumberBox class
28770 * @cfg {String} headline Box headline
28771 * @cfg {String} content Box content
28772 * @cfg {String} icon Box icon
28773 * @cfg {String} footer Footer text
28774 * @cfg {String} fhref Footer href
28777 * Create a new NumberBox
28778 * @param {Object} config The config object
28782 Roo.bootstrap.dash.NumberBox = function(config){
28783 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28787 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28796 getAutoCreate : function(){
28800 cls : 'small-box ',
28808 cls : 'roo-headline',
28809 html : this.headline
28813 cls : 'roo-content',
28814 html : this.content
28828 cls : 'ion ' + this.icon
28837 cls : 'small-box-footer',
28838 href : this.fhref || '#',
28842 cfg.cn.push(footer);
28849 onRender : function(ct,position){
28850 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28857 setHeadline: function (value)
28859 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28862 setFooter: function (value, href)
28864 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28867 this.el.select('a.small-box-footer',true).first().attr('href', href);
28872 setContent: function (value)
28874 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28877 initEvents: function()
28891 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28894 * @class Roo.bootstrap.dash.TabBox
28895 * @extends Roo.bootstrap.Component
28896 * Bootstrap TabBox class
28897 * @cfg {String} title Title of the TabBox
28898 * @cfg {String} icon Icon of the TabBox
28899 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28900 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28903 * Create a new TabBox
28904 * @param {Object} config The config object
28908 Roo.bootstrap.dash.TabBox = function(config){
28909 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28914 * When a pane is added
28915 * @param {Roo.bootstrap.dash.TabPane} pane
28919 * @event activatepane
28920 * When a pane is activated
28921 * @param {Roo.bootstrap.dash.TabPane} pane
28923 "activatepane" : true
28931 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28936 tabScrollable : false,
28938 getChildContainer : function()
28940 return this.el.select('.tab-content', true).first();
28943 getAutoCreate : function(){
28947 cls: 'pull-left header',
28955 cls: 'fa ' + this.icon
28961 cls: 'nav nav-tabs pull-right',
28967 if(this.tabScrollable){
28974 cls: 'nav nav-tabs pull-right',
28985 cls: 'nav-tabs-custom',
28990 cls: 'tab-content no-padding',
28998 initEvents : function()
29000 //Roo.log('add add pane handler');
29001 this.on('addpane', this.onAddPane, this);
29004 * Updates the box title
29005 * @param {String} html to set the title to.
29007 setTitle : function(value)
29009 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29011 onAddPane : function(pane)
29013 this.panes.push(pane);
29014 //Roo.log('addpane');
29016 // tabs are rendere left to right..
29017 if(!this.showtabs){
29021 var ctr = this.el.select('.nav-tabs', true).first();
29024 var existing = ctr.select('.nav-tab',true);
29025 var qty = existing.getCount();;
29028 var tab = ctr.createChild({
29030 cls : 'nav-tab' + (qty ? '' : ' active'),
29038 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29041 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29043 pane.el.addClass('active');
29048 onTabClick : function(ev,un,ob,pane)
29050 //Roo.log('tab - prev default');
29051 ev.preventDefault();
29054 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29055 pane.tab.addClass('active');
29056 //Roo.log(pane.title);
29057 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29058 // technically we should have a deactivate event.. but maybe add later.
29059 // and it should not de-activate the selected tab...
29060 this.fireEvent('activatepane', pane);
29061 pane.el.addClass('active');
29062 pane.fireEvent('activate');
29067 getActivePane : function()
29070 Roo.each(this.panes, function(p) {
29071 if(p.el.hasClass('active')){
29092 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29094 * @class Roo.bootstrap.TabPane
29095 * @extends Roo.bootstrap.Component
29096 * Bootstrap TabPane class
29097 * @cfg {Boolean} active (false | true) Default false
29098 * @cfg {String} title title of panel
29102 * Create a new TabPane
29103 * @param {Object} config The config object
29106 Roo.bootstrap.dash.TabPane = function(config){
29107 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29113 * When a pane is activated
29114 * @param {Roo.bootstrap.dash.TabPane} pane
29121 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29126 // the tabBox that this is attached to.
29129 getAutoCreate : function()
29137 cfg.cls += ' active';
29142 initEvents : function()
29144 //Roo.log('trigger add pane handler');
29145 this.parent().fireEvent('addpane', this)
29149 * Updates the tab title
29150 * @param {String} html to set the title to.
29152 setTitle: function(str)
29158 this.tab.select('a', true).first().dom.innerHTML = str;
29175 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29178 * @class Roo.bootstrap.menu.Menu
29179 * @extends Roo.bootstrap.Component
29180 * Bootstrap Menu class - container for Menu
29181 * @cfg {String} html Text of the menu
29182 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29183 * @cfg {String} icon Font awesome icon
29184 * @cfg {String} pos Menu align to (top | bottom) default bottom
29188 * Create a new Menu
29189 * @param {Object} config The config object
29193 Roo.bootstrap.menu.Menu = function(config){
29194 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29198 * @event beforeshow
29199 * Fires before this menu is displayed
29200 * @param {Roo.bootstrap.menu.Menu} this
29204 * @event beforehide
29205 * Fires before this menu is hidden
29206 * @param {Roo.bootstrap.menu.Menu} this
29211 * Fires after this menu is displayed
29212 * @param {Roo.bootstrap.menu.Menu} this
29217 * Fires after this menu is hidden
29218 * @param {Roo.bootstrap.menu.Menu} this
29223 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29224 * @param {Roo.bootstrap.menu.Menu} this
29225 * @param {Roo.EventObject} e
29232 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29236 weight : 'default',
29241 getChildContainer : function() {
29242 if(this.isSubMenu){
29246 return this.el.select('ul.dropdown-menu', true).first();
29249 getAutoCreate : function()
29254 cls : 'roo-menu-text',
29262 cls : 'fa ' + this.icon
29273 cls : 'dropdown-button btn btn-' + this.weight,
29278 cls : 'dropdown-toggle btn btn-' + this.weight,
29288 cls : 'dropdown-menu'
29294 if(this.pos == 'top'){
29295 cfg.cls += ' dropup';
29298 if(this.isSubMenu){
29301 cls : 'dropdown-menu'
29308 onRender : function(ct, position)
29310 this.isSubMenu = ct.hasClass('dropdown-submenu');
29312 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29315 initEvents : function()
29317 if(this.isSubMenu){
29321 this.hidden = true;
29323 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29324 this.triggerEl.on('click', this.onTriggerPress, this);
29326 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29327 this.buttonEl.on('click', this.onClick, this);
29333 if(this.isSubMenu){
29337 return this.el.select('ul.dropdown-menu', true).first();
29340 onClick : function(e)
29342 this.fireEvent("click", this, e);
29345 onTriggerPress : function(e)
29347 if (this.isVisible()) {
29354 isVisible : function(){
29355 return !this.hidden;
29360 this.fireEvent("beforeshow", this);
29362 this.hidden = false;
29363 this.el.addClass('open');
29365 Roo.get(document).on("mouseup", this.onMouseUp, this);
29367 this.fireEvent("show", this);
29374 this.fireEvent("beforehide", this);
29376 this.hidden = true;
29377 this.el.removeClass('open');
29379 Roo.get(document).un("mouseup", this.onMouseUp);
29381 this.fireEvent("hide", this);
29384 onMouseUp : function()
29398 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29401 * @class Roo.bootstrap.menu.Item
29402 * @extends Roo.bootstrap.Component
29403 * Bootstrap MenuItem class
29404 * @cfg {Boolean} submenu (true | false) default false
29405 * @cfg {String} html text of the item
29406 * @cfg {String} href the link
29407 * @cfg {Boolean} disable (true | false) default false
29408 * @cfg {Boolean} preventDefault (true | false) default true
29409 * @cfg {String} icon Font awesome icon
29410 * @cfg {String} pos Submenu align to (left | right) default right
29414 * Create a new Item
29415 * @param {Object} config The config object
29419 Roo.bootstrap.menu.Item = function(config){
29420 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29424 * Fires when the mouse is hovering over this menu
29425 * @param {Roo.bootstrap.menu.Item} this
29426 * @param {Roo.EventObject} e
29431 * Fires when the mouse exits this menu
29432 * @param {Roo.bootstrap.menu.Item} this
29433 * @param {Roo.EventObject} e
29439 * The raw click event for the entire grid.
29440 * @param {Roo.EventObject} e
29446 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29451 preventDefault: true,
29456 getAutoCreate : function()
29461 cls : 'roo-menu-item-text',
29469 cls : 'fa ' + this.icon
29478 href : this.href || '#',
29485 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29489 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29491 if(this.pos == 'left'){
29492 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29499 initEvents : function()
29501 this.el.on('mouseover', this.onMouseOver, this);
29502 this.el.on('mouseout', this.onMouseOut, this);
29504 this.el.select('a', true).first().on('click', this.onClick, this);
29508 onClick : function(e)
29510 if(this.preventDefault){
29511 e.preventDefault();
29514 this.fireEvent("click", this, e);
29517 onMouseOver : function(e)
29519 if(this.submenu && this.pos == 'left'){
29520 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29523 this.fireEvent("mouseover", this, e);
29526 onMouseOut : function(e)
29528 this.fireEvent("mouseout", this, e);
29540 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29543 * @class Roo.bootstrap.menu.Separator
29544 * @extends Roo.bootstrap.Component
29545 * Bootstrap Separator class
29548 * Create a new Separator
29549 * @param {Object} config The config object
29553 Roo.bootstrap.menu.Separator = function(config){
29554 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29557 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29559 getAutoCreate : function(){
29562 cls: 'dropdown-divider divider'
29580 * @class Roo.bootstrap.Tooltip
29581 * Bootstrap Tooltip class
29582 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29583 * to determine which dom element triggers the tooltip.
29585 * It needs to add support for additional attributes like tooltip-position
29588 * Create a new Toolti
29589 * @param {Object} config The config object
29592 Roo.bootstrap.Tooltip = function(config){
29593 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29595 this.alignment = Roo.bootstrap.Tooltip.alignment;
29597 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29598 this.alignment = config.alignment;
29603 Roo.apply(Roo.bootstrap.Tooltip, {
29605 * @function init initialize tooltip monitoring.
29609 currentTip : false,
29610 currentRegion : false,
29616 Roo.get(document).on('mouseover', this.enter ,this);
29617 Roo.get(document).on('mouseout', this.leave, this);
29620 this.currentTip = new Roo.bootstrap.Tooltip();
29623 enter : function(ev)
29625 var dom = ev.getTarget();
29627 //Roo.log(['enter',dom]);
29628 var el = Roo.fly(dom);
29629 if (this.currentEl) {
29631 //Roo.log(this.currentEl);
29632 //Roo.log(this.currentEl.contains(dom));
29633 if (this.currentEl == el) {
29636 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29642 if (this.currentTip.el) {
29643 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29647 if(!el || el.dom == document){
29653 if (!el.attr('tooltip')) {
29654 pel = el.findParent("[tooltip]");
29656 bindEl = Roo.get(pel);
29662 // you can not look for children, as if el is the body.. then everythign is the child..
29663 if (!pel && !el.attr('tooltip')) { //
29664 if (!el.select("[tooltip]").elements.length) {
29667 // is the mouse over this child...?
29668 bindEl = el.select("[tooltip]").first();
29669 var xy = ev.getXY();
29670 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29671 //Roo.log("not in region.");
29674 //Roo.log("child element over..");
29677 this.currentEl = el;
29678 this.currentTip.bind(bindEl);
29679 this.currentRegion = Roo.lib.Region.getRegion(dom);
29680 this.currentTip.enter();
29683 leave : function(ev)
29685 var dom = ev.getTarget();
29686 //Roo.log(['leave',dom]);
29687 if (!this.currentEl) {
29692 if (dom != this.currentEl.dom) {
29695 var xy = ev.getXY();
29696 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29699 // only activate leave if mouse cursor is outside... bounding box..
29704 if (this.currentTip) {
29705 this.currentTip.leave();
29707 //Roo.log('clear currentEl');
29708 this.currentEl = false;
29713 'left' : ['r-l', [-2,0], 'right'],
29714 'right' : ['l-r', [2,0], 'left'],
29715 'bottom' : ['t-b', [0,2], 'top'],
29716 'top' : [ 'b-t', [0,-2], 'bottom']
29722 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29727 delay : null, // can be { show : 300 , hide: 500}
29731 hoverState : null, //???
29733 placement : 'bottom',
29737 getAutoCreate : function(){
29744 cls : 'tooltip-arrow arrow'
29747 cls : 'tooltip-inner'
29754 bind : function(el)
29759 initEvents : function()
29761 this.arrowEl = this.el.select('.arrow', true).first();
29762 this.innerEl = this.el.select('.tooltip-inner', true).first();
29765 enter : function () {
29767 if (this.timeout != null) {
29768 clearTimeout(this.timeout);
29771 this.hoverState = 'in';
29772 //Roo.log("enter - show");
29773 if (!this.delay || !this.delay.show) {
29778 this.timeout = setTimeout(function () {
29779 if (_t.hoverState == 'in') {
29782 }, this.delay.show);
29786 clearTimeout(this.timeout);
29788 this.hoverState = 'out';
29789 if (!this.delay || !this.delay.hide) {
29795 this.timeout = setTimeout(function () {
29796 //Roo.log("leave - timeout");
29798 if (_t.hoverState == 'out') {
29800 Roo.bootstrap.Tooltip.currentEl = false;
29805 show : function (msg)
29808 this.render(document.body);
29811 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29813 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29815 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29817 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29818 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29820 var placement = typeof this.placement == 'function' ?
29821 this.placement.call(this, this.el, on_el) :
29824 var autoToken = /\s?auto?\s?/i;
29825 var autoPlace = autoToken.test(placement);
29827 placement = placement.replace(autoToken, '') || 'top';
29831 //this.el.setXY([0,0]);
29833 //this.el.dom.style.display='block';
29835 //this.el.appendTo(on_el);
29837 var p = this.getPosition();
29838 var box = this.el.getBox();
29844 var align = this.alignment[placement];
29846 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29848 if(placement == 'top' || placement == 'bottom'){
29850 placement = 'right';
29853 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29854 placement = 'left';
29857 var scroll = Roo.select('body', true).first().getScroll();
29859 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29863 align = this.alignment[placement];
29865 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29869 var elems = document.getElementsByTagName('div');
29870 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29871 for (var i = 0; i < elems.length; i++) {
29872 var zindex = Number.parseInt(
29873 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29876 if (zindex > highest) {
29883 this.el.dom.style.zIndex = highest;
29885 this.el.alignTo(this.bindEl, align[0],align[1]);
29886 //var arrow = this.el.select('.arrow',true).first();
29887 //arrow.set(align[2],
29889 this.el.addClass(placement);
29890 this.el.addClass("bs-tooltip-"+ placement);
29892 this.el.addClass('in fade show');
29894 this.hoverState = null;
29896 if (this.el.hasClass('fade')) {
29911 //this.el.setXY([0,0]);
29912 this.el.removeClass(['show', 'in']);
29928 * @class Roo.bootstrap.LocationPicker
29929 * @extends Roo.bootstrap.Component
29930 * Bootstrap LocationPicker class
29931 * @cfg {Number} latitude Position when init default 0
29932 * @cfg {Number} longitude Position when init default 0
29933 * @cfg {Number} zoom default 15
29934 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29935 * @cfg {Boolean} mapTypeControl default false
29936 * @cfg {Boolean} disableDoubleClickZoom default false
29937 * @cfg {Boolean} scrollwheel default true
29938 * @cfg {Boolean} streetViewControl default false
29939 * @cfg {Number} radius default 0
29940 * @cfg {String} locationName
29941 * @cfg {Boolean} draggable default true
29942 * @cfg {Boolean} enableAutocomplete default false
29943 * @cfg {Boolean} enableReverseGeocode default true
29944 * @cfg {String} markerTitle
29947 * Create a new LocationPicker
29948 * @param {Object} config The config object
29952 Roo.bootstrap.LocationPicker = function(config){
29954 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29959 * Fires when the picker initialized.
29960 * @param {Roo.bootstrap.LocationPicker} this
29961 * @param {Google Location} location
29965 * @event positionchanged
29966 * Fires when the picker position changed.
29967 * @param {Roo.bootstrap.LocationPicker} this
29968 * @param {Google Location} location
29970 positionchanged : true,
29973 * Fires when the map resize.
29974 * @param {Roo.bootstrap.LocationPicker} this
29979 * Fires when the map show.
29980 * @param {Roo.bootstrap.LocationPicker} this
29985 * Fires when the map hide.
29986 * @param {Roo.bootstrap.LocationPicker} this
29991 * Fires when click the map.
29992 * @param {Roo.bootstrap.LocationPicker} this
29993 * @param {Map event} e
29997 * @event mapRightClick
29998 * Fires when right click the map.
29999 * @param {Roo.bootstrap.LocationPicker} this
30000 * @param {Map event} e
30002 mapRightClick : true,
30004 * @event markerClick
30005 * Fires when click the marker.
30006 * @param {Roo.bootstrap.LocationPicker} this
30007 * @param {Map event} e
30009 markerClick : true,
30011 * @event markerRightClick
30012 * Fires when right click the marker.
30013 * @param {Roo.bootstrap.LocationPicker} this
30014 * @param {Map event} e
30016 markerRightClick : true,
30018 * @event OverlayViewDraw
30019 * Fires when OverlayView Draw
30020 * @param {Roo.bootstrap.LocationPicker} this
30022 OverlayViewDraw : true,
30024 * @event OverlayViewOnAdd
30025 * Fires when OverlayView Draw
30026 * @param {Roo.bootstrap.LocationPicker} this
30028 OverlayViewOnAdd : true,
30030 * @event OverlayViewOnRemove
30031 * Fires when OverlayView Draw
30032 * @param {Roo.bootstrap.LocationPicker} this
30034 OverlayViewOnRemove : true,
30036 * @event OverlayViewShow
30037 * Fires when OverlayView Draw
30038 * @param {Roo.bootstrap.LocationPicker} this
30039 * @param {Pixel} cpx
30041 OverlayViewShow : true,
30043 * @event OverlayViewHide
30044 * Fires when OverlayView Draw
30045 * @param {Roo.bootstrap.LocationPicker} this
30047 OverlayViewHide : true,
30049 * @event loadexception
30050 * Fires when load google lib failed.
30051 * @param {Roo.bootstrap.LocationPicker} this
30053 loadexception : true
30058 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30060 gMapContext: false,
30066 mapTypeControl: false,
30067 disableDoubleClickZoom: false,
30069 streetViewControl: false,
30073 enableAutocomplete: false,
30074 enableReverseGeocode: true,
30077 getAutoCreate: function()
30082 cls: 'roo-location-picker'
30088 initEvents: function(ct, position)
30090 if(!this.el.getWidth() || this.isApplied()){
30094 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30099 initial: function()
30101 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30102 this.fireEvent('loadexception', this);
30106 if(!this.mapTypeId){
30107 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30110 this.gMapContext = this.GMapContext();
30112 this.initOverlayView();
30114 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30118 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30119 _this.setPosition(_this.gMapContext.marker.position);
30122 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30123 _this.fireEvent('mapClick', this, event);
30127 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30128 _this.fireEvent('mapRightClick', this, event);
30132 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30133 _this.fireEvent('markerClick', this, event);
30137 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30138 _this.fireEvent('markerRightClick', this, event);
30142 this.setPosition(this.gMapContext.location);
30144 this.fireEvent('initial', this, this.gMapContext.location);
30147 initOverlayView: function()
30151 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30155 _this.fireEvent('OverlayViewDraw', _this);
30160 _this.fireEvent('OverlayViewOnAdd', _this);
30163 onRemove: function()
30165 _this.fireEvent('OverlayViewOnRemove', _this);
30168 show: function(cpx)
30170 _this.fireEvent('OverlayViewShow', _this, cpx);
30175 _this.fireEvent('OverlayViewHide', _this);
30181 fromLatLngToContainerPixel: function(event)
30183 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30186 isApplied: function()
30188 return this.getGmapContext() == false ? false : true;
30191 getGmapContext: function()
30193 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30196 GMapContext: function()
30198 var position = new google.maps.LatLng(this.latitude, this.longitude);
30200 var _map = new google.maps.Map(this.el.dom, {
30203 mapTypeId: this.mapTypeId,
30204 mapTypeControl: this.mapTypeControl,
30205 disableDoubleClickZoom: this.disableDoubleClickZoom,
30206 scrollwheel: this.scrollwheel,
30207 streetViewControl: this.streetViewControl,
30208 locationName: this.locationName,
30209 draggable: this.draggable,
30210 enableAutocomplete: this.enableAutocomplete,
30211 enableReverseGeocode: this.enableReverseGeocode
30214 var _marker = new google.maps.Marker({
30215 position: position,
30217 title: this.markerTitle,
30218 draggable: this.draggable
30225 location: position,
30226 radius: this.radius,
30227 locationName: this.locationName,
30228 addressComponents: {
30229 formatted_address: null,
30230 addressLine1: null,
30231 addressLine2: null,
30233 streetNumber: null,
30237 stateOrProvince: null
30240 domContainer: this.el.dom,
30241 geodecoder: new google.maps.Geocoder()
30245 drawCircle: function(center, radius, options)
30247 if (this.gMapContext.circle != null) {
30248 this.gMapContext.circle.setMap(null);
30252 options = Roo.apply({}, options, {
30253 strokeColor: "#0000FF",
30254 strokeOpacity: .35,
30256 fillColor: "#0000FF",
30260 options.map = this.gMapContext.map;
30261 options.radius = radius;
30262 options.center = center;
30263 this.gMapContext.circle = new google.maps.Circle(options);
30264 return this.gMapContext.circle;
30270 setPosition: function(location)
30272 this.gMapContext.location = location;
30273 this.gMapContext.marker.setPosition(location);
30274 this.gMapContext.map.panTo(location);
30275 this.drawCircle(location, this.gMapContext.radius, {});
30279 if (this.gMapContext.settings.enableReverseGeocode) {
30280 this.gMapContext.geodecoder.geocode({
30281 latLng: this.gMapContext.location
30282 }, function(results, status) {
30284 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30285 _this.gMapContext.locationName = results[0].formatted_address;
30286 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30288 _this.fireEvent('positionchanged', this, location);
30295 this.fireEvent('positionchanged', this, location);
30300 google.maps.event.trigger(this.gMapContext.map, "resize");
30302 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30304 this.fireEvent('resize', this);
30307 setPositionByLatLng: function(latitude, longitude)
30309 this.setPosition(new google.maps.LatLng(latitude, longitude));
30312 getCurrentPosition: function()
30315 latitude: this.gMapContext.location.lat(),
30316 longitude: this.gMapContext.location.lng()
30320 getAddressName: function()
30322 return this.gMapContext.locationName;
30325 getAddressComponents: function()
30327 return this.gMapContext.addressComponents;
30330 address_component_from_google_geocode: function(address_components)
30334 for (var i = 0; i < address_components.length; i++) {
30335 var component = address_components[i];
30336 if (component.types.indexOf("postal_code") >= 0) {
30337 result.postalCode = component.short_name;
30338 } else if (component.types.indexOf("street_number") >= 0) {
30339 result.streetNumber = component.short_name;
30340 } else if (component.types.indexOf("route") >= 0) {
30341 result.streetName = component.short_name;
30342 } else if (component.types.indexOf("neighborhood") >= 0) {
30343 result.city = component.short_name;
30344 } else if (component.types.indexOf("locality") >= 0) {
30345 result.city = component.short_name;
30346 } else if (component.types.indexOf("sublocality") >= 0) {
30347 result.district = component.short_name;
30348 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30349 result.stateOrProvince = component.short_name;
30350 } else if (component.types.indexOf("country") >= 0) {
30351 result.country = component.short_name;
30355 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30356 result.addressLine2 = "";
30360 setZoomLevel: function(zoom)
30362 this.gMapContext.map.setZoom(zoom);
30375 this.fireEvent('show', this);
30386 this.fireEvent('hide', this);
30391 Roo.apply(Roo.bootstrap.LocationPicker, {
30393 OverlayView : function(map, options)
30395 options = options || {};
30402 * @class Roo.bootstrap.Alert
30403 * @extends Roo.bootstrap.Component
30404 * Bootstrap Alert class - shows an alert area box
30406 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30407 Enter a valid email address
30410 * @cfg {String} title The title of alert
30411 * @cfg {String} html The content of alert
30412 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30413 * @cfg {String} fa font-awesomeicon
30414 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30415 * @cfg {Boolean} close true to show a x closer
30419 * Create a new alert
30420 * @param {Object} config The config object
30424 Roo.bootstrap.Alert = function(config){
30425 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30429 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30435 faicon: false, // BC
30439 getAutoCreate : function()
30451 style : this.close ? '' : 'display:none'
30455 cls : 'roo-alert-icon'
30460 cls : 'roo-alert-title',
30465 cls : 'roo-alert-text',
30472 cfg.cn[0].cls += ' fa ' + this.faicon;
30475 cfg.cn[0].cls += ' fa ' + this.fa;
30479 cfg.cls += ' alert-' + this.weight;
30485 initEvents: function()
30487 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30488 this.titleEl = this.el.select('.roo-alert-title',true).first();
30489 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30490 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30491 if (this.seconds > 0) {
30492 this.hide.defer(this.seconds, this);
30496 * Set the Title Message HTML
30497 * @param {String} html
30499 setTitle : function(str)
30501 this.titleEl.dom.innerHTML = str;
30505 * Set the Body Message HTML
30506 * @param {String} html
30508 setHtml : function(str)
30510 this.htmlEl.dom.innerHTML = str;
30513 * Set the Weight of the alert
30514 * @param {String} (success|info|warning|danger) weight
30517 setWeight : function(weight)
30520 this.el.removeClass('alert-' + this.weight);
30523 this.weight = weight;
30525 this.el.addClass('alert-' + this.weight);
30528 * Set the Icon of the alert
30529 * @param {String} see fontawsome names (name without the 'fa-' bit)
30531 setIcon : function(icon)
30534 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30537 this.faicon = icon;
30539 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30564 * @class Roo.bootstrap.UploadCropbox
30565 * @extends Roo.bootstrap.Component
30566 * Bootstrap UploadCropbox class
30567 * @cfg {String} emptyText show when image has been loaded
30568 * @cfg {String} rotateNotify show when image too small to rotate
30569 * @cfg {Number} errorTimeout default 3000
30570 * @cfg {Number} minWidth default 300
30571 * @cfg {Number} minHeight default 300
30572 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30573 * @cfg {Boolean} isDocument (true|false) default false
30574 * @cfg {String} url action url
30575 * @cfg {String} paramName default 'imageUpload'
30576 * @cfg {String} method default POST
30577 * @cfg {Boolean} loadMask (true|false) default true
30578 * @cfg {Boolean} loadingText default 'Loading...'
30581 * Create a new UploadCropbox
30582 * @param {Object} config The config object
30585 Roo.bootstrap.UploadCropbox = function(config){
30586 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30590 * @event beforeselectfile
30591 * Fire before select file
30592 * @param {Roo.bootstrap.UploadCropbox} this
30594 "beforeselectfile" : true,
30597 * Fire after initEvent
30598 * @param {Roo.bootstrap.UploadCropbox} this
30603 * Fire after initEvent
30604 * @param {Roo.bootstrap.UploadCropbox} this
30605 * @param {String} data
30610 * Fire when preparing the file data
30611 * @param {Roo.bootstrap.UploadCropbox} this
30612 * @param {Object} file
30617 * Fire when get exception
30618 * @param {Roo.bootstrap.UploadCropbox} this
30619 * @param {XMLHttpRequest} xhr
30621 "exception" : true,
30623 * @event beforeloadcanvas
30624 * Fire before load the canvas
30625 * @param {Roo.bootstrap.UploadCropbox} this
30626 * @param {String} src
30628 "beforeloadcanvas" : true,
30631 * Fire when trash image
30632 * @param {Roo.bootstrap.UploadCropbox} this
30637 * Fire when download the image
30638 * @param {Roo.bootstrap.UploadCropbox} this
30642 * @event footerbuttonclick
30643 * Fire when footerbuttonclick
30644 * @param {Roo.bootstrap.UploadCropbox} this
30645 * @param {String} type
30647 "footerbuttonclick" : true,
30651 * @param {Roo.bootstrap.UploadCropbox} this
30656 * Fire when rotate the image
30657 * @param {Roo.bootstrap.UploadCropbox} this
30658 * @param {String} pos
30663 * Fire when inspect the file
30664 * @param {Roo.bootstrap.UploadCropbox} this
30665 * @param {Object} file
30670 * Fire when xhr upload the file
30671 * @param {Roo.bootstrap.UploadCropbox} this
30672 * @param {Object} data
30677 * Fire when arrange the file data
30678 * @param {Roo.bootstrap.UploadCropbox} this
30679 * @param {Object} formData
30684 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30687 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30689 emptyText : 'Click to upload image',
30690 rotateNotify : 'Image is too small to rotate',
30691 errorTimeout : 3000,
30705 cropType : 'image/jpeg',
30707 canvasLoaded : false,
30708 isDocument : false,
30710 paramName : 'imageUpload',
30712 loadingText : 'Loading...',
30715 getAutoCreate : function()
30719 cls : 'roo-upload-cropbox',
30723 cls : 'roo-upload-cropbox-selector',
30728 cls : 'roo-upload-cropbox-body',
30729 style : 'cursor:pointer',
30733 cls : 'roo-upload-cropbox-preview'
30737 cls : 'roo-upload-cropbox-thumb'
30741 cls : 'roo-upload-cropbox-empty-notify',
30742 html : this.emptyText
30746 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30747 html : this.rotateNotify
30753 cls : 'roo-upload-cropbox-footer',
30756 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30766 onRender : function(ct, position)
30768 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30770 if (this.buttons.length) {
30772 Roo.each(this.buttons, function(bb) {
30774 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30776 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30782 this.maskEl = this.el;
30786 initEvents : function()
30788 this.urlAPI = (window.createObjectURL && window) ||
30789 (window.URL && URL.revokeObjectURL && URL) ||
30790 (window.webkitURL && webkitURL);
30792 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30793 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30795 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30796 this.selectorEl.hide();
30798 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30799 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30801 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30802 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30803 this.thumbEl.hide();
30805 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30806 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30808 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30809 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30810 this.errorEl.hide();
30812 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30813 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30814 this.footerEl.hide();
30816 this.setThumbBoxSize();
30822 this.fireEvent('initial', this);
30829 window.addEventListener("resize", function() { _this.resize(); } );
30831 this.bodyEl.on('click', this.beforeSelectFile, this);
30834 this.bodyEl.on('touchstart', this.onTouchStart, this);
30835 this.bodyEl.on('touchmove', this.onTouchMove, this);
30836 this.bodyEl.on('touchend', this.onTouchEnd, this);
30840 this.bodyEl.on('mousedown', this.onMouseDown, this);
30841 this.bodyEl.on('mousemove', this.onMouseMove, this);
30842 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30843 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30844 Roo.get(document).on('mouseup', this.onMouseUp, this);
30847 this.selectorEl.on('change', this.onFileSelected, this);
30853 this.baseScale = 1;
30855 this.baseRotate = 1;
30856 this.dragable = false;
30857 this.pinching = false;
30860 this.cropData = false;
30861 this.notifyEl.dom.innerHTML = this.emptyText;
30863 this.selectorEl.dom.value = '';
30867 resize : function()
30869 if(this.fireEvent('resize', this) != false){
30870 this.setThumbBoxPosition();
30871 this.setCanvasPosition();
30875 onFooterButtonClick : function(e, el, o, type)
30878 case 'rotate-left' :
30879 this.onRotateLeft(e);
30881 case 'rotate-right' :
30882 this.onRotateRight(e);
30885 this.beforeSelectFile(e);
30900 this.fireEvent('footerbuttonclick', this, type);
30903 beforeSelectFile : function(e)
30905 e.preventDefault();
30907 if(this.fireEvent('beforeselectfile', this) != false){
30908 this.selectorEl.dom.click();
30912 onFileSelected : function(e)
30914 e.preventDefault();
30916 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30920 var file = this.selectorEl.dom.files[0];
30922 if(this.fireEvent('inspect', this, file) != false){
30923 this.prepare(file);
30928 trash : function(e)
30930 this.fireEvent('trash', this);
30933 download : function(e)
30935 this.fireEvent('download', this);
30938 loadCanvas : function(src)
30940 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30944 this.imageEl = document.createElement('img');
30948 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30950 this.imageEl.src = src;
30954 onLoadCanvas : function()
30956 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30957 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30959 this.bodyEl.un('click', this.beforeSelectFile, this);
30961 this.notifyEl.hide();
30962 this.thumbEl.show();
30963 this.footerEl.show();
30965 this.baseRotateLevel();
30967 if(this.isDocument){
30968 this.setThumbBoxSize();
30971 this.setThumbBoxPosition();
30973 this.baseScaleLevel();
30979 this.canvasLoaded = true;
30982 this.maskEl.unmask();
30987 setCanvasPosition : function()
30989 if(!this.canvasEl){
30993 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30994 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30996 this.previewEl.setLeft(pw);
30997 this.previewEl.setTop(ph);
31001 onMouseDown : function(e)
31005 this.dragable = true;
31006 this.pinching = false;
31008 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31009 this.dragable = false;
31013 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31014 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31018 onMouseMove : function(e)
31022 if(!this.canvasLoaded){
31026 if (!this.dragable){
31030 var minX = Math.ceil(this.thumbEl.getLeft(true));
31031 var minY = Math.ceil(this.thumbEl.getTop(true));
31033 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31034 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31036 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31037 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31039 x = x - this.mouseX;
31040 y = y - this.mouseY;
31042 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31043 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31045 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31046 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31048 this.previewEl.setLeft(bgX);
31049 this.previewEl.setTop(bgY);
31051 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31052 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31055 onMouseUp : function(e)
31059 this.dragable = false;
31062 onMouseWheel : function(e)
31066 this.startScale = this.scale;
31068 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31070 if(!this.zoomable()){
31071 this.scale = this.startScale;
31080 zoomable : function()
31082 var minScale = this.thumbEl.getWidth() / this.minWidth;
31084 if(this.minWidth < this.minHeight){
31085 minScale = this.thumbEl.getHeight() / this.minHeight;
31088 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31089 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31093 (this.rotate == 0 || this.rotate == 180) &&
31095 width > this.imageEl.OriginWidth ||
31096 height > this.imageEl.OriginHeight ||
31097 (width < this.minWidth && height < this.minHeight)
31105 (this.rotate == 90 || this.rotate == 270) &&
31107 width > this.imageEl.OriginWidth ||
31108 height > this.imageEl.OriginHeight ||
31109 (width < this.minHeight && height < this.minWidth)
31116 !this.isDocument &&
31117 (this.rotate == 0 || this.rotate == 180) &&
31119 width < this.minWidth ||
31120 width > this.imageEl.OriginWidth ||
31121 height < this.minHeight ||
31122 height > this.imageEl.OriginHeight
31129 !this.isDocument &&
31130 (this.rotate == 90 || this.rotate == 270) &&
31132 width < this.minHeight ||
31133 width > this.imageEl.OriginWidth ||
31134 height < this.minWidth ||
31135 height > this.imageEl.OriginHeight
31145 onRotateLeft : function(e)
31147 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31149 var minScale = this.thumbEl.getWidth() / this.minWidth;
31151 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31152 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31154 this.startScale = this.scale;
31156 while (this.getScaleLevel() < minScale){
31158 this.scale = this.scale + 1;
31160 if(!this.zoomable()){
31165 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31166 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31171 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31178 this.scale = this.startScale;
31180 this.onRotateFail();
31185 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31187 if(this.isDocument){
31188 this.setThumbBoxSize();
31189 this.setThumbBoxPosition();
31190 this.setCanvasPosition();
31195 this.fireEvent('rotate', this, 'left');
31199 onRotateRight : function(e)
31201 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31203 var minScale = this.thumbEl.getWidth() / this.minWidth;
31205 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31206 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31208 this.startScale = this.scale;
31210 while (this.getScaleLevel() < minScale){
31212 this.scale = this.scale + 1;
31214 if(!this.zoomable()){
31219 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31220 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31225 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31232 this.scale = this.startScale;
31234 this.onRotateFail();
31239 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31241 if(this.isDocument){
31242 this.setThumbBoxSize();
31243 this.setThumbBoxPosition();
31244 this.setCanvasPosition();
31249 this.fireEvent('rotate', this, 'right');
31252 onRotateFail : function()
31254 this.errorEl.show(true);
31258 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31263 this.previewEl.dom.innerHTML = '';
31265 var canvasEl = document.createElement("canvas");
31267 var contextEl = canvasEl.getContext("2d");
31269 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31270 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31271 var center = this.imageEl.OriginWidth / 2;
31273 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31274 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31275 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31276 center = this.imageEl.OriginHeight / 2;
31279 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31281 contextEl.translate(center, center);
31282 contextEl.rotate(this.rotate * Math.PI / 180);
31284 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31286 this.canvasEl = document.createElement("canvas");
31288 this.contextEl = this.canvasEl.getContext("2d");
31290 switch (this.rotate) {
31293 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31294 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31296 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31301 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31302 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31304 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31305 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);
31309 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31314 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31315 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31317 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31318 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);
31322 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);
31327 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31328 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31330 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31331 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31335 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);
31342 this.previewEl.appendChild(this.canvasEl);
31344 this.setCanvasPosition();
31349 if(!this.canvasLoaded){
31353 var imageCanvas = document.createElement("canvas");
31355 var imageContext = imageCanvas.getContext("2d");
31357 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31358 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31360 var center = imageCanvas.width / 2;
31362 imageContext.translate(center, center);
31364 imageContext.rotate(this.rotate * Math.PI / 180);
31366 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31368 var canvas = document.createElement("canvas");
31370 var context = canvas.getContext("2d");
31372 canvas.width = this.minWidth;
31373 canvas.height = this.minHeight;
31375 switch (this.rotate) {
31378 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31379 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31381 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31382 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31384 var targetWidth = this.minWidth - 2 * x;
31385 var targetHeight = this.minHeight - 2 * y;
31389 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31390 scale = targetWidth / width;
31393 if(x > 0 && y == 0){
31394 scale = targetHeight / height;
31397 if(x > 0 && y > 0){
31398 scale = targetWidth / width;
31400 if(width < height){
31401 scale = targetHeight / height;
31405 context.scale(scale, scale);
31407 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31408 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31410 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31411 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31413 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31418 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31419 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31421 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31422 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31424 var targetWidth = this.minWidth - 2 * x;
31425 var targetHeight = this.minHeight - 2 * y;
31429 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31430 scale = targetWidth / width;
31433 if(x > 0 && y == 0){
31434 scale = targetHeight / height;
31437 if(x > 0 && y > 0){
31438 scale = targetWidth / width;
31440 if(width < height){
31441 scale = targetHeight / height;
31445 context.scale(scale, scale);
31447 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31448 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31450 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31451 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31453 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31455 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31460 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31461 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31463 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31464 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31466 var targetWidth = this.minWidth - 2 * x;
31467 var targetHeight = this.minHeight - 2 * y;
31471 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31472 scale = targetWidth / width;
31475 if(x > 0 && y == 0){
31476 scale = targetHeight / height;
31479 if(x > 0 && y > 0){
31480 scale = targetWidth / width;
31482 if(width < height){
31483 scale = targetHeight / height;
31487 context.scale(scale, scale);
31489 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31490 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31492 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31493 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31495 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31496 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31498 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31503 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31504 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31506 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31507 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31509 var targetWidth = this.minWidth - 2 * x;
31510 var targetHeight = this.minHeight - 2 * y;
31514 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31515 scale = targetWidth / width;
31518 if(x > 0 && y == 0){
31519 scale = targetHeight / height;
31522 if(x > 0 && y > 0){
31523 scale = targetWidth / width;
31525 if(width < height){
31526 scale = targetHeight / height;
31530 context.scale(scale, scale);
31532 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31533 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31535 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31536 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31538 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31540 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31547 this.cropData = canvas.toDataURL(this.cropType);
31549 if(this.fireEvent('crop', this, this.cropData) !== false){
31550 this.process(this.file, this.cropData);
31557 setThumbBoxSize : function()
31561 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31562 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31563 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31565 this.minWidth = width;
31566 this.minHeight = height;
31568 if(this.rotate == 90 || this.rotate == 270){
31569 this.minWidth = height;
31570 this.minHeight = width;
31575 width = Math.ceil(this.minWidth * height / this.minHeight);
31577 if(this.minWidth > this.minHeight){
31579 height = Math.ceil(this.minHeight * width / this.minWidth);
31582 this.thumbEl.setStyle({
31583 width : width + 'px',
31584 height : height + 'px'
31591 setThumbBoxPosition : function()
31593 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31594 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31596 this.thumbEl.setLeft(x);
31597 this.thumbEl.setTop(y);
31601 baseRotateLevel : function()
31603 this.baseRotate = 1;
31606 typeof(this.exif) != 'undefined' &&
31607 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31608 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31610 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31613 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31617 baseScaleLevel : function()
31621 if(this.isDocument){
31623 if(this.baseRotate == 6 || this.baseRotate == 8){
31625 height = this.thumbEl.getHeight();
31626 this.baseScale = height / this.imageEl.OriginWidth;
31628 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31629 width = this.thumbEl.getWidth();
31630 this.baseScale = width / this.imageEl.OriginHeight;
31636 height = this.thumbEl.getHeight();
31637 this.baseScale = height / this.imageEl.OriginHeight;
31639 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31640 width = this.thumbEl.getWidth();
31641 this.baseScale = width / this.imageEl.OriginWidth;
31647 if(this.baseRotate == 6 || this.baseRotate == 8){
31649 width = this.thumbEl.getHeight();
31650 this.baseScale = width / this.imageEl.OriginHeight;
31652 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31653 height = this.thumbEl.getWidth();
31654 this.baseScale = height / this.imageEl.OriginHeight;
31657 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31658 height = this.thumbEl.getWidth();
31659 this.baseScale = height / this.imageEl.OriginHeight;
31661 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31662 width = this.thumbEl.getHeight();
31663 this.baseScale = width / this.imageEl.OriginWidth;
31670 width = this.thumbEl.getWidth();
31671 this.baseScale = width / this.imageEl.OriginWidth;
31673 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31674 height = this.thumbEl.getHeight();
31675 this.baseScale = height / this.imageEl.OriginHeight;
31678 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31680 height = this.thumbEl.getHeight();
31681 this.baseScale = height / this.imageEl.OriginHeight;
31683 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31684 width = this.thumbEl.getWidth();
31685 this.baseScale = width / this.imageEl.OriginWidth;
31693 getScaleLevel : function()
31695 return this.baseScale * Math.pow(1.1, this.scale);
31698 onTouchStart : function(e)
31700 if(!this.canvasLoaded){
31701 this.beforeSelectFile(e);
31705 var touches = e.browserEvent.touches;
31711 if(touches.length == 1){
31712 this.onMouseDown(e);
31716 if(touches.length != 2){
31722 for(var i = 0, finger; finger = touches[i]; i++){
31723 coords.push(finger.pageX, finger.pageY);
31726 var x = Math.pow(coords[0] - coords[2], 2);
31727 var y = Math.pow(coords[1] - coords[3], 2);
31729 this.startDistance = Math.sqrt(x + y);
31731 this.startScale = this.scale;
31733 this.pinching = true;
31734 this.dragable = false;
31738 onTouchMove : function(e)
31740 if(!this.pinching && !this.dragable){
31744 var touches = e.browserEvent.touches;
31751 this.onMouseMove(e);
31757 for(var i = 0, finger; finger = touches[i]; i++){
31758 coords.push(finger.pageX, finger.pageY);
31761 var x = Math.pow(coords[0] - coords[2], 2);
31762 var y = Math.pow(coords[1] - coords[3], 2);
31764 this.endDistance = Math.sqrt(x + y);
31766 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31768 if(!this.zoomable()){
31769 this.scale = this.startScale;
31777 onTouchEnd : function(e)
31779 this.pinching = false;
31780 this.dragable = false;
31784 process : function(file, crop)
31787 this.maskEl.mask(this.loadingText);
31790 this.xhr = new XMLHttpRequest();
31792 file.xhr = this.xhr;
31794 this.xhr.open(this.method, this.url, true);
31797 "Accept": "application/json",
31798 "Cache-Control": "no-cache",
31799 "X-Requested-With": "XMLHttpRequest"
31802 for (var headerName in headers) {
31803 var headerValue = headers[headerName];
31805 this.xhr.setRequestHeader(headerName, headerValue);
31811 this.xhr.onload = function()
31813 _this.xhrOnLoad(_this.xhr);
31816 this.xhr.onerror = function()
31818 _this.xhrOnError(_this.xhr);
31821 var formData = new FormData();
31823 formData.append('returnHTML', 'NO');
31826 formData.append('crop', crop);
31829 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31830 formData.append(this.paramName, file, file.name);
31833 if(typeof(file.filename) != 'undefined'){
31834 formData.append('filename', file.filename);
31837 if(typeof(file.mimetype) != 'undefined'){
31838 formData.append('mimetype', file.mimetype);
31841 if(this.fireEvent('arrange', this, formData) != false){
31842 this.xhr.send(formData);
31846 xhrOnLoad : function(xhr)
31849 this.maskEl.unmask();
31852 if (xhr.readyState !== 4) {
31853 this.fireEvent('exception', this, xhr);
31857 var response = Roo.decode(xhr.responseText);
31859 if(!response.success){
31860 this.fireEvent('exception', this, xhr);
31864 var response = Roo.decode(xhr.responseText);
31866 this.fireEvent('upload', this, response);
31870 xhrOnError : function()
31873 this.maskEl.unmask();
31876 Roo.log('xhr on error');
31878 var response = Roo.decode(xhr.responseText);
31884 prepare : function(file)
31887 this.maskEl.mask(this.loadingText);
31893 if(typeof(file) === 'string'){
31894 this.loadCanvas(file);
31898 if(!file || !this.urlAPI){
31903 this.cropType = file.type;
31907 if(this.fireEvent('prepare', this, this.file) != false){
31909 var reader = new FileReader();
31911 reader.onload = function (e) {
31912 if (e.target.error) {
31913 Roo.log(e.target.error);
31917 var buffer = e.target.result,
31918 dataView = new DataView(buffer),
31920 maxOffset = dataView.byteLength - 4,
31924 if (dataView.getUint16(0) === 0xffd8) {
31925 while (offset < maxOffset) {
31926 markerBytes = dataView.getUint16(offset);
31928 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31929 markerLength = dataView.getUint16(offset + 2) + 2;
31930 if (offset + markerLength > dataView.byteLength) {
31931 Roo.log('Invalid meta data: Invalid segment size.');
31935 if(markerBytes == 0xffe1){
31936 _this.parseExifData(
31943 offset += markerLength;
31953 var url = _this.urlAPI.createObjectURL(_this.file);
31955 _this.loadCanvas(url);
31960 reader.readAsArrayBuffer(this.file);
31966 parseExifData : function(dataView, offset, length)
31968 var tiffOffset = offset + 10,
31972 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31973 // No Exif data, might be XMP data instead
31977 // Check for the ASCII code for "Exif" (0x45786966):
31978 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31979 // No Exif data, might be XMP data instead
31982 if (tiffOffset + 8 > dataView.byteLength) {
31983 Roo.log('Invalid Exif data: Invalid segment size.');
31986 // Check for the two null bytes:
31987 if (dataView.getUint16(offset + 8) !== 0x0000) {
31988 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31991 // Check the byte alignment:
31992 switch (dataView.getUint16(tiffOffset)) {
31994 littleEndian = true;
31997 littleEndian = false;
32000 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32003 // Check for the TIFF tag marker (0x002A):
32004 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32005 Roo.log('Invalid Exif data: Missing TIFF marker.');
32008 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32009 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32011 this.parseExifTags(
32014 tiffOffset + dirOffset,
32019 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32024 if (dirOffset + 6 > dataView.byteLength) {
32025 Roo.log('Invalid Exif data: Invalid directory offset.');
32028 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32029 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32030 if (dirEndOffset + 4 > dataView.byteLength) {
32031 Roo.log('Invalid Exif data: Invalid directory size.');
32034 for (i = 0; i < tagsNumber; i += 1) {
32038 dirOffset + 2 + 12 * i, // tag offset
32042 // Return the offset to the next directory:
32043 return dataView.getUint32(dirEndOffset, littleEndian);
32046 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32048 var tag = dataView.getUint16(offset, littleEndian);
32050 this.exif[tag] = this.getExifValue(
32054 dataView.getUint16(offset + 2, littleEndian), // tag type
32055 dataView.getUint32(offset + 4, littleEndian), // tag length
32060 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32062 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32071 Roo.log('Invalid Exif data: Invalid tag type.');
32075 tagSize = tagType.size * length;
32076 // Determine if the value is contained in the dataOffset bytes,
32077 // or if the value at the dataOffset is a pointer to the actual data:
32078 dataOffset = tagSize > 4 ?
32079 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32080 if (dataOffset + tagSize > dataView.byteLength) {
32081 Roo.log('Invalid Exif data: Invalid data offset.');
32084 if (length === 1) {
32085 return tagType.getValue(dataView, dataOffset, littleEndian);
32088 for (i = 0; i < length; i += 1) {
32089 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32092 if (tagType.ascii) {
32094 // Concatenate the chars:
32095 for (i = 0; i < values.length; i += 1) {
32097 // Ignore the terminating NULL byte(s):
32098 if (c === '\u0000') {
32110 Roo.apply(Roo.bootstrap.UploadCropbox, {
32112 'Orientation': 0x0112
32116 1: 0, //'top-left',
32118 3: 180, //'bottom-right',
32119 // 4: 'bottom-left',
32121 6: 90, //'right-top',
32122 // 7: 'right-bottom',
32123 8: 270 //'left-bottom'
32127 // byte, 8-bit unsigned int:
32129 getValue: function (dataView, dataOffset) {
32130 return dataView.getUint8(dataOffset);
32134 // ascii, 8-bit byte:
32136 getValue: function (dataView, dataOffset) {
32137 return String.fromCharCode(dataView.getUint8(dataOffset));
32142 // short, 16 bit int:
32144 getValue: function (dataView, dataOffset, littleEndian) {
32145 return dataView.getUint16(dataOffset, littleEndian);
32149 // long, 32 bit int:
32151 getValue: function (dataView, dataOffset, littleEndian) {
32152 return dataView.getUint32(dataOffset, littleEndian);
32156 // rational = two long values, first is numerator, second is denominator:
32158 getValue: function (dataView, dataOffset, littleEndian) {
32159 return dataView.getUint32(dataOffset, littleEndian) /
32160 dataView.getUint32(dataOffset + 4, littleEndian);
32164 // slong, 32 bit signed int:
32166 getValue: function (dataView, dataOffset, littleEndian) {
32167 return dataView.getInt32(dataOffset, littleEndian);
32171 // srational, two slongs, first is numerator, second is denominator:
32173 getValue: function (dataView, dataOffset, littleEndian) {
32174 return dataView.getInt32(dataOffset, littleEndian) /
32175 dataView.getInt32(dataOffset + 4, littleEndian);
32185 cls : 'btn-group roo-upload-cropbox-rotate-left',
32186 action : 'rotate-left',
32190 cls : 'btn btn-default',
32191 html : '<i class="fa fa-undo"></i>'
32197 cls : 'btn-group roo-upload-cropbox-picture',
32198 action : 'picture',
32202 cls : 'btn btn-default',
32203 html : '<i class="fa fa-picture-o"></i>'
32209 cls : 'btn-group roo-upload-cropbox-rotate-right',
32210 action : 'rotate-right',
32214 cls : 'btn btn-default',
32215 html : '<i class="fa fa-repeat"></i>'
32223 cls : 'btn-group roo-upload-cropbox-rotate-left',
32224 action : 'rotate-left',
32228 cls : 'btn btn-default',
32229 html : '<i class="fa fa-undo"></i>'
32235 cls : 'btn-group roo-upload-cropbox-download',
32236 action : 'download',
32240 cls : 'btn btn-default',
32241 html : '<i class="fa fa-download"></i>'
32247 cls : 'btn-group roo-upload-cropbox-crop',
32252 cls : 'btn btn-default',
32253 html : '<i class="fa fa-crop"></i>'
32259 cls : 'btn-group roo-upload-cropbox-trash',
32264 cls : 'btn btn-default',
32265 html : '<i class="fa fa-trash"></i>'
32271 cls : 'btn-group roo-upload-cropbox-rotate-right',
32272 action : 'rotate-right',
32276 cls : 'btn btn-default',
32277 html : '<i class="fa fa-repeat"></i>'
32285 cls : 'btn-group roo-upload-cropbox-rotate-left',
32286 action : 'rotate-left',
32290 cls : 'btn btn-default',
32291 html : '<i class="fa fa-undo"></i>'
32297 cls : 'btn-group roo-upload-cropbox-rotate-right',
32298 action : 'rotate-right',
32302 cls : 'btn btn-default',
32303 html : '<i class="fa fa-repeat"></i>'
32316 * @class Roo.bootstrap.DocumentManager
32317 * @extends Roo.bootstrap.Component
32318 * Bootstrap DocumentManager class
32319 * @cfg {String} paramName default 'imageUpload'
32320 * @cfg {String} toolTipName default 'filename'
32321 * @cfg {String} method default POST
32322 * @cfg {String} url action url
32323 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32324 * @cfg {Boolean} multiple multiple upload default true
32325 * @cfg {Number} thumbSize default 300
32326 * @cfg {String} fieldLabel
32327 * @cfg {Number} labelWidth default 4
32328 * @cfg {String} labelAlign (left|top) default left
32329 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32330 * @cfg {Number} labellg set the width of label (1-12)
32331 * @cfg {Number} labelmd set the width of label (1-12)
32332 * @cfg {Number} labelsm set the width of label (1-12)
32333 * @cfg {Number} labelxs set the width of label (1-12)
32336 * Create a new DocumentManager
32337 * @param {Object} config The config object
32340 Roo.bootstrap.DocumentManager = function(config){
32341 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32344 this.delegates = [];
32349 * Fire when initial the DocumentManager
32350 * @param {Roo.bootstrap.DocumentManager} this
32355 * inspect selected file
32356 * @param {Roo.bootstrap.DocumentManager} this
32357 * @param {File} file
32362 * Fire when xhr load exception
32363 * @param {Roo.bootstrap.DocumentManager} this
32364 * @param {XMLHttpRequest} xhr
32366 "exception" : true,
32368 * @event afterupload
32369 * Fire when xhr load exception
32370 * @param {Roo.bootstrap.DocumentManager} this
32371 * @param {XMLHttpRequest} xhr
32373 "afterupload" : true,
32376 * prepare the form data
32377 * @param {Roo.bootstrap.DocumentManager} this
32378 * @param {Object} formData
32383 * Fire when remove the file
32384 * @param {Roo.bootstrap.DocumentManager} this
32385 * @param {Object} file
32390 * Fire after refresh the file
32391 * @param {Roo.bootstrap.DocumentManager} this
32396 * Fire after click the image
32397 * @param {Roo.bootstrap.DocumentManager} this
32398 * @param {Object} file
32403 * Fire when upload a image and editable set to true
32404 * @param {Roo.bootstrap.DocumentManager} this
32405 * @param {Object} file
32409 * @event beforeselectfile
32410 * Fire before select file
32411 * @param {Roo.bootstrap.DocumentManager} this
32413 "beforeselectfile" : true,
32416 * Fire before process file
32417 * @param {Roo.bootstrap.DocumentManager} this
32418 * @param {Object} file
32422 * @event previewrendered
32423 * Fire when preview rendered
32424 * @param {Roo.bootstrap.DocumentManager} this
32425 * @param {Object} file
32427 "previewrendered" : true,
32430 "previewResize" : true
32435 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32444 paramName : 'imageUpload',
32445 toolTipName : 'filename',
32448 labelAlign : 'left',
32458 getAutoCreate : function()
32460 var managerWidget = {
32462 cls : 'roo-document-manager',
32466 cls : 'roo-document-manager-selector',
32471 cls : 'roo-document-manager-uploader',
32475 cls : 'roo-document-manager-upload-btn',
32476 html : '<i class="fa fa-plus"></i>'
32487 cls : 'column col-md-12',
32492 if(this.fieldLabel.length){
32497 cls : 'column col-md-12',
32498 html : this.fieldLabel
32502 cls : 'column col-md-12',
32507 if(this.labelAlign == 'left'){
32512 html : this.fieldLabel
32521 if(this.labelWidth > 12){
32522 content[0].style = "width: " + this.labelWidth + 'px';
32525 if(this.labelWidth < 13 && this.labelmd == 0){
32526 this.labelmd = this.labelWidth;
32529 if(this.labellg > 0){
32530 content[0].cls += ' col-lg-' + this.labellg;
32531 content[1].cls += ' col-lg-' + (12 - this.labellg);
32534 if(this.labelmd > 0){
32535 content[0].cls += ' col-md-' + this.labelmd;
32536 content[1].cls += ' col-md-' + (12 - this.labelmd);
32539 if(this.labelsm > 0){
32540 content[0].cls += ' col-sm-' + this.labelsm;
32541 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32544 if(this.labelxs > 0){
32545 content[0].cls += ' col-xs-' + this.labelxs;
32546 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32554 cls : 'row clearfix',
32562 initEvents : function()
32564 this.managerEl = this.el.select('.roo-document-manager', true).first();
32565 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32567 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32568 this.selectorEl.hide();
32571 this.selectorEl.attr('multiple', 'multiple');
32574 this.selectorEl.on('change', this.onFileSelected, this);
32576 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32577 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32579 this.uploader.on('click', this.onUploaderClick, this);
32581 this.renderProgressDialog();
32585 window.addEventListener("resize", function() { _this.refresh(); } );
32587 this.fireEvent('initial', this);
32590 renderProgressDialog : function()
32594 this.progressDialog = new Roo.bootstrap.Modal({
32595 cls : 'roo-document-manager-progress-dialog',
32596 allow_close : false,
32607 btnclick : function() {
32608 _this.uploadCancel();
32614 this.progressDialog.render(Roo.get(document.body));
32616 this.progress = new Roo.bootstrap.Progress({
32617 cls : 'roo-document-manager-progress',
32622 this.progress.render(this.progressDialog.getChildContainer());
32624 this.progressBar = new Roo.bootstrap.ProgressBar({
32625 cls : 'roo-document-manager-progress-bar',
32628 aria_valuemax : 12,
32632 this.progressBar.render(this.progress.getChildContainer());
32635 onUploaderClick : function(e)
32637 e.preventDefault();
32639 if(this.fireEvent('beforeselectfile', this) != false){
32640 this.selectorEl.dom.click();
32645 onFileSelected : function(e)
32647 e.preventDefault();
32649 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32653 Roo.each(this.selectorEl.dom.files, function(file){
32654 if(this.fireEvent('inspect', this, file) != false){
32655 this.files.push(file);
32665 this.selectorEl.dom.value = '';
32667 if(!this.files || !this.files.length){
32671 if(this.boxes > 0 && this.files.length > this.boxes){
32672 this.files = this.files.slice(0, this.boxes);
32675 this.uploader.show();
32677 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32678 this.uploader.hide();
32687 Roo.each(this.files, function(file){
32689 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32690 var f = this.renderPreview(file);
32695 if(file.type.indexOf('image') != -1){
32696 this.delegates.push(
32698 _this.process(file);
32699 }).createDelegate(this)
32707 _this.process(file);
32708 }).createDelegate(this)
32713 this.files = files;
32715 this.delegates = this.delegates.concat(docs);
32717 if(!this.delegates.length){
32722 this.progressBar.aria_valuemax = this.delegates.length;
32729 arrange : function()
32731 if(!this.delegates.length){
32732 this.progressDialog.hide();
32737 var delegate = this.delegates.shift();
32739 this.progressDialog.show();
32741 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32743 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32748 refresh : function()
32750 this.uploader.show();
32752 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32753 this.uploader.hide();
32756 Roo.isTouch ? this.closable(false) : this.closable(true);
32758 this.fireEvent('refresh', this);
32761 onRemove : function(e, el, o)
32763 e.preventDefault();
32765 this.fireEvent('remove', this, o);
32769 remove : function(o)
32773 Roo.each(this.files, function(file){
32774 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32783 this.files = files;
32790 Roo.each(this.files, function(file){
32795 file.target.remove();
32804 onClick : function(e, el, o)
32806 e.preventDefault();
32808 this.fireEvent('click', this, o);
32812 closable : function(closable)
32814 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32816 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32828 xhrOnLoad : function(xhr)
32830 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32834 if (xhr.readyState !== 4) {
32836 this.fireEvent('exception', this, xhr);
32840 var response = Roo.decode(xhr.responseText);
32842 if(!response.success){
32844 this.fireEvent('exception', this, xhr);
32848 var file = this.renderPreview(response.data);
32850 this.files.push(file);
32854 this.fireEvent('afterupload', this, xhr);
32858 xhrOnError : function(xhr)
32860 Roo.log('xhr on error');
32862 var response = Roo.decode(xhr.responseText);
32869 process : function(file)
32871 if(this.fireEvent('process', this, file) !== false){
32872 if(this.editable && file.type.indexOf('image') != -1){
32873 this.fireEvent('edit', this, file);
32877 this.uploadStart(file, false);
32884 uploadStart : function(file, crop)
32886 this.xhr = new XMLHttpRequest();
32888 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32893 file.xhr = this.xhr;
32895 this.managerEl.createChild({
32897 cls : 'roo-document-manager-loading',
32901 tooltip : file.name,
32902 cls : 'roo-document-manager-thumb',
32903 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32909 this.xhr.open(this.method, this.url, true);
32912 "Accept": "application/json",
32913 "Cache-Control": "no-cache",
32914 "X-Requested-With": "XMLHttpRequest"
32917 for (var headerName in headers) {
32918 var headerValue = headers[headerName];
32920 this.xhr.setRequestHeader(headerName, headerValue);
32926 this.xhr.onload = function()
32928 _this.xhrOnLoad(_this.xhr);
32931 this.xhr.onerror = function()
32933 _this.xhrOnError(_this.xhr);
32936 var formData = new FormData();
32938 formData.append('returnHTML', 'NO');
32941 formData.append('crop', crop);
32944 formData.append(this.paramName, file, file.name);
32951 if(this.fireEvent('prepare', this, formData, options) != false){
32953 if(options.manually){
32957 this.xhr.send(formData);
32961 this.uploadCancel();
32964 uploadCancel : function()
32970 this.delegates = [];
32972 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32979 renderPreview : function(file)
32981 if(typeof(file.target) != 'undefined' && file.target){
32985 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32987 var previewEl = this.managerEl.createChild({
32989 cls : 'roo-document-manager-preview',
32993 tooltip : file[this.toolTipName],
32994 cls : 'roo-document-manager-thumb',
32995 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33000 html : '<i class="fa fa-times-circle"></i>'
33005 var close = previewEl.select('button.close', true).first();
33007 close.on('click', this.onRemove, this, file);
33009 file.target = previewEl;
33011 var image = previewEl.select('img', true).first();
33015 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33017 image.on('click', this.onClick, this, file);
33019 this.fireEvent('previewrendered', this, file);
33025 onPreviewLoad : function(file, image)
33027 if(typeof(file.target) == 'undefined' || !file.target){
33031 var width = image.dom.naturalWidth || image.dom.width;
33032 var height = image.dom.naturalHeight || image.dom.height;
33034 if(!this.previewResize) {
33038 if(width > height){
33039 file.target.addClass('wide');
33043 file.target.addClass('tall');
33048 uploadFromSource : function(file, crop)
33050 this.xhr = new XMLHttpRequest();
33052 this.managerEl.createChild({
33054 cls : 'roo-document-manager-loading',
33058 tooltip : file.name,
33059 cls : 'roo-document-manager-thumb',
33060 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33066 this.xhr.open(this.method, this.url, true);
33069 "Accept": "application/json",
33070 "Cache-Control": "no-cache",
33071 "X-Requested-With": "XMLHttpRequest"
33074 for (var headerName in headers) {
33075 var headerValue = headers[headerName];
33077 this.xhr.setRequestHeader(headerName, headerValue);
33083 this.xhr.onload = function()
33085 _this.xhrOnLoad(_this.xhr);
33088 this.xhr.onerror = function()
33090 _this.xhrOnError(_this.xhr);
33093 var formData = new FormData();
33095 formData.append('returnHTML', 'NO');
33097 formData.append('crop', crop);
33099 if(typeof(file.filename) != 'undefined'){
33100 formData.append('filename', file.filename);
33103 if(typeof(file.mimetype) != 'undefined'){
33104 formData.append('mimetype', file.mimetype);
33109 if(this.fireEvent('prepare', this, formData) != false){
33110 this.xhr.send(formData);
33120 * @class Roo.bootstrap.DocumentViewer
33121 * @extends Roo.bootstrap.Component
33122 * Bootstrap DocumentViewer class
33123 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33124 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33127 * Create a new DocumentViewer
33128 * @param {Object} config The config object
33131 Roo.bootstrap.DocumentViewer = function(config){
33132 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33137 * Fire after initEvent
33138 * @param {Roo.bootstrap.DocumentViewer} this
33144 * @param {Roo.bootstrap.DocumentViewer} this
33149 * Fire after download button
33150 * @param {Roo.bootstrap.DocumentViewer} this
33155 * Fire after trash button
33156 * @param {Roo.bootstrap.DocumentViewer} this
33163 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33165 showDownload : true,
33169 getAutoCreate : function()
33173 cls : 'roo-document-viewer',
33177 cls : 'roo-document-viewer-body',
33181 cls : 'roo-document-viewer-thumb',
33185 cls : 'roo-document-viewer-image'
33193 cls : 'roo-document-viewer-footer',
33196 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33200 cls : 'btn-group roo-document-viewer-download',
33204 cls : 'btn btn-default',
33205 html : '<i class="fa fa-download"></i>'
33211 cls : 'btn-group roo-document-viewer-trash',
33215 cls : 'btn btn-default',
33216 html : '<i class="fa fa-trash"></i>'
33229 initEvents : function()
33231 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33232 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33234 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33235 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33237 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33238 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33240 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33241 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33243 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33244 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33246 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33247 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33249 this.bodyEl.on('click', this.onClick, this);
33250 this.downloadBtn.on('click', this.onDownload, this);
33251 this.trashBtn.on('click', this.onTrash, this);
33253 this.downloadBtn.hide();
33254 this.trashBtn.hide();
33256 if(this.showDownload){
33257 this.downloadBtn.show();
33260 if(this.showTrash){
33261 this.trashBtn.show();
33264 if(!this.showDownload && !this.showTrash) {
33265 this.footerEl.hide();
33270 initial : function()
33272 this.fireEvent('initial', this);
33276 onClick : function(e)
33278 e.preventDefault();
33280 this.fireEvent('click', this);
33283 onDownload : function(e)
33285 e.preventDefault();
33287 this.fireEvent('download', this);
33290 onTrash : function(e)
33292 e.preventDefault();
33294 this.fireEvent('trash', this);
33306 * @class Roo.bootstrap.NavProgressBar
33307 * @extends Roo.bootstrap.Component
33308 * Bootstrap NavProgressBar class
33311 * Create a new nav progress bar
33312 * @param {Object} config The config object
33315 Roo.bootstrap.NavProgressBar = function(config){
33316 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33318 this.bullets = this.bullets || [];
33320 // Roo.bootstrap.NavProgressBar.register(this);
33324 * Fires when the active item changes
33325 * @param {Roo.bootstrap.NavProgressBar} this
33326 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33327 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33334 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33339 getAutoCreate : function()
33341 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33345 cls : 'roo-navigation-bar-group',
33349 cls : 'roo-navigation-top-bar'
33353 cls : 'roo-navigation-bullets-bar',
33357 cls : 'roo-navigation-bar'
33364 cls : 'roo-navigation-bottom-bar'
33374 initEvents: function()
33379 onRender : function(ct, position)
33381 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33383 if(this.bullets.length){
33384 Roo.each(this.bullets, function(b){
33393 addItem : function(cfg)
33395 var item = new Roo.bootstrap.NavProgressItem(cfg);
33397 item.parentId = this.id;
33398 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33401 var top = new Roo.bootstrap.Element({
33403 cls : 'roo-navigation-bar-text'
33406 var bottom = new Roo.bootstrap.Element({
33408 cls : 'roo-navigation-bar-text'
33411 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33412 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33414 var topText = new Roo.bootstrap.Element({
33416 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33419 var bottomText = new Roo.bootstrap.Element({
33421 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33424 topText.onRender(top.el, null);
33425 bottomText.onRender(bottom.el, null);
33428 item.bottomEl = bottom;
33431 this.barItems.push(item);
33436 getActive : function()
33438 var active = false;
33440 Roo.each(this.barItems, function(v){
33442 if (!v.isActive()) {
33454 setActiveItem : function(item)
33458 Roo.each(this.barItems, function(v){
33459 if (v.rid == item.rid) {
33463 if (v.isActive()) {
33464 v.setActive(false);
33469 item.setActive(true);
33471 this.fireEvent('changed', this, item, prev);
33474 getBarItem: function(rid)
33478 Roo.each(this.barItems, function(e) {
33479 if (e.rid != rid) {
33490 indexOfItem : function(item)
33494 Roo.each(this.barItems, function(v, i){
33496 if (v.rid != item.rid) {
33507 setActiveNext : function()
33509 var i = this.indexOfItem(this.getActive());
33511 if (i > this.barItems.length) {
33515 this.setActiveItem(this.barItems[i+1]);
33518 setActivePrev : function()
33520 var i = this.indexOfItem(this.getActive());
33526 this.setActiveItem(this.barItems[i-1]);
33529 format : function()
33531 if(!this.barItems.length){
33535 var width = 100 / this.barItems.length;
33537 Roo.each(this.barItems, function(i){
33538 i.el.setStyle('width', width + '%');
33539 i.topEl.el.setStyle('width', width + '%');
33540 i.bottomEl.el.setStyle('width', width + '%');
33549 * Nav Progress Item
33554 * @class Roo.bootstrap.NavProgressItem
33555 * @extends Roo.bootstrap.Component
33556 * Bootstrap NavProgressItem class
33557 * @cfg {String} rid the reference id
33558 * @cfg {Boolean} active (true|false) Is item active default false
33559 * @cfg {Boolean} disabled (true|false) Is item active default false
33560 * @cfg {String} html
33561 * @cfg {String} position (top|bottom) text position default bottom
33562 * @cfg {String} icon show icon instead of number
33565 * Create a new NavProgressItem
33566 * @param {Object} config The config object
33568 Roo.bootstrap.NavProgressItem = function(config){
33569 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33574 * The raw click event for the entire grid.
33575 * @param {Roo.bootstrap.NavProgressItem} this
33576 * @param {Roo.EventObject} e
33583 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33589 position : 'bottom',
33592 getAutoCreate : function()
33594 var iconCls = 'roo-navigation-bar-item-icon';
33596 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33600 cls: 'roo-navigation-bar-item',
33610 cfg.cls += ' active';
33613 cfg.cls += ' disabled';
33619 disable : function()
33621 this.setDisabled(true);
33624 enable : function()
33626 this.setDisabled(false);
33629 initEvents: function()
33631 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33633 this.iconEl.on('click', this.onClick, this);
33636 onClick : function(e)
33638 e.preventDefault();
33644 if(this.fireEvent('click', this, e) === false){
33648 this.parent().setActiveItem(this);
33651 isActive: function ()
33653 return this.active;
33656 setActive : function(state)
33658 if(this.active == state){
33662 this.active = state;
33665 this.el.addClass('active');
33669 this.el.removeClass('active');
33674 setDisabled : function(state)
33676 if(this.disabled == state){
33680 this.disabled = state;
33683 this.el.addClass('disabled');
33687 this.el.removeClass('disabled');
33690 tooltipEl : function()
33692 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33705 * @class Roo.bootstrap.FieldLabel
33706 * @extends Roo.bootstrap.Component
33707 * Bootstrap FieldLabel class
33708 * @cfg {String} html contents of the element
33709 * @cfg {String} tag tag of the element default label
33710 * @cfg {String} cls class of the element
33711 * @cfg {String} target label target
33712 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33713 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33714 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33715 * @cfg {String} iconTooltip default "This field is required"
33716 * @cfg {String} indicatorpos (left|right) default left
33719 * Create a new FieldLabel
33720 * @param {Object} config The config object
33723 Roo.bootstrap.FieldLabel = function(config){
33724 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33729 * Fires after the field has been marked as invalid.
33730 * @param {Roo.form.FieldLabel} this
33731 * @param {String} msg The validation message
33736 * Fires after the field has been validated with no errors.
33737 * @param {Roo.form.FieldLabel} this
33743 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33750 invalidClass : 'has-warning',
33751 validClass : 'has-success',
33752 iconTooltip : 'This field is required',
33753 indicatorpos : 'left',
33755 getAutoCreate : function(){
33758 if (!this.allowBlank) {
33764 cls : 'roo-bootstrap-field-label ' + this.cls,
33769 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33770 tooltip : this.iconTooltip
33779 if(this.indicatorpos == 'right'){
33782 cls : 'roo-bootstrap-field-label ' + this.cls,
33791 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33792 tooltip : this.iconTooltip
33801 initEvents: function()
33803 Roo.bootstrap.Element.superclass.initEvents.call(this);
33805 this.indicator = this.indicatorEl();
33807 if(this.indicator){
33808 this.indicator.removeClass('visible');
33809 this.indicator.addClass('invisible');
33812 Roo.bootstrap.FieldLabel.register(this);
33815 indicatorEl : function()
33817 var indicator = this.el.select('i.roo-required-indicator',true).first();
33828 * Mark this field as valid
33830 markValid : function()
33832 if(this.indicator){
33833 this.indicator.removeClass('visible');
33834 this.indicator.addClass('invisible');
33836 if (Roo.bootstrap.version == 3) {
33837 this.el.removeClass(this.invalidClass);
33838 this.el.addClass(this.validClass);
33840 this.el.removeClass('is-invalid');
33841 this.el.addClass('is-valid');
33845 this.fireEvent('valid', this);
33849 * Mark this field as invalid
33850 * @param {String} msg The validation message
33852 markInvalid : function(msg)
33854 if(this.indicator){
33855 this.indicator.removeClass('invisible');
33856 this.indicator.addClass('visible');
33858 if (Roo.bootstrap.version == 3) {
33859 this.el.removeClass(this.validClass);
33860 this.el.addClass(this.invalidClass);
33862 this.el.removeClass('is-valid');
33863 this.el.addClass('is-invalid');
33867 this.fireEvent('invalid', this, msg);
33873 Roo.apply(Roo.bootstrap.FieldLabel, {
33878 * register a FieldLabel Group
33879 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33881 register : function(label)
33883 if(this.groups.hasOwnProperty(label.target)){
33887 this.groups[label.target] = label;
33891 * fetch a FieldLabel Group based on the target
33892 * @param {string} target
33893 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33895 get: function(target) {
33896 if (typeof(this.groups[target]) == 'undefined') {
33900 return this.groups[target] ;
33909 * page DateSplitField.
33915 * @class Roo.bootstrap.DateSplitField
33916 * @extends Roo.bootstrap.Component
33917 * Bootstrap DateSplitField class
33918 * @cfg {string} fieldLabel - the label associated
33919 * @cfg {Number} labelWidth set the width of label (0-12)
33920 * @cfg {String} labelAlign (top|left)
33921 * @cfg {Boolean} dayAllowBlank (true|false) default false
33922 * @cfg {Boolean} monthAllowBlank (true|false) default false
33923 * @cfg {Boolean} yearAllowBlank (true|false) default false
33924 * @cfg {string} dayPlaceholder
33925 * @cfg {string} monthPlaceholder
33926 * @cfg {string} yearPlaceholder
33927 * @cfg {string} dayFormat default 'd'
33928 * @cfg {string} monthFormat default 'm'
33929 * @cfg {string} yearFormat default 'Y'
33930 * @cfg {Number} labellg set the width of label (1-12)
33931 * @cfg {Number} labelmd set the width of label (1-12)
33932 * @cfg {Number} labelsm set the width of label (1-12)
33933 * @cfg {Number} labelxs set the width of label (1-12)
33937 * Create a new DateSplitField
33938 * @param {Object} config The config object
33941 Roo.bootstrap.DateSplitField = function(config){
33942 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33948 * getting the data of years
33949 * @param {Roo.bootstrap.DateSplitField} this
33950 * @param {Object} years
33955 * getting the data of days
33956 * @param {Roo.bootstrap.DateSplitField} this
33957 * @param {Object} days
33962 * Fires after the field has been marked as invalid.
33963 * @param {Roo.form.Field} this
33964 * @param {String} msg The validation message
33969 * Fires after the field has been validated with no errors.
33970 * @param {Roo.form.Field} this
33976 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33979 labelAlign : 'top',
33981 dayAllowBlank : false,
33982 monthAllowBlank : false,
33983 yearAllowBlank : false,
33984 dayPlaceholder : '',
33985 monthPlaceholder : '',
33986 yearPlaceholder : '',
33990 isFormField : true,
33996 getAutoCreate : function()
34000 cls : 'row roo-date-split-field-group',
34005 cls : 'form-hidden-field roo-date-split-field-group-value',
34011 var labelCls = 'col-md-12';
34012 var contentCls = 'col-md-4';
34014 if(this.fieldLabel){
34018 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34022 html : this.fieldLabel
34027 if(this.labelAlign == 'left'){
34029 if(this.labelWidth > 12){
34030 label.style = "width: " + this.labelWidth + 'px';
34033 if(this.labelWidth < 13 && this.labelmd == 0){
34034 this.labelmd = this.labelWidth;
34037 if(this.labellg > 0){
34038 labelCls = ' col-lg-' + this.labellg;
34039 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34042 if(this.labelmd > 0){
34043 labelCls = ' col-md-' + this.labelmd;
34044 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34047 if(this.labelsm > 0){
34048 labelCls = ' col-sm-' + this.labelsm;
34049 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34052 if(this.labelxs > 0){
34053 labelCls = ' col-xs-' + this.labelxs;
34054 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34058 label.cls += ' ' + labelCls;
34060 cfg.cn.push(label);
34063 Roo.each(['day', 'month', 'year'], function(t){
34066 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34073 inputEl: function ()
34075 return this.el.select('.roo-date-split-field-group-value', true).first();
34078 onRender : function(ct, position)
34082 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34084 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34086 this.dayField = new Roo.bootstrap.ComboBox({
34087 allowBlank : this.dayAllowBlank,
34088 alwaysQuery : true,
34089 displayField : 'value',
34092 forceSelection : true,
34094 placeholder : this.dayPlaceholder,
34095 selectOnFocus : true,
34096 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34097 triggerAction : 'all',
34099 valueField : 'value',
34100 store : new Roo.data.SimpleStore({
34101 data : (function() {
34103 _this.fireEvent('days', _this, days);
34106 fields : [ 'value' ]
34109 select : function (_self, record, index)
34111 _this.setValue(_this.getValue());
34116 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34118 this.monthField = new Roo.bootstrap.MonthField({
34119 after : '<i class=\"fa fa-calendar\"></i>',
34120 allowBlank : this.monthAllowBlank,
34121 placeholder : this.monthPlaceholder,
34124 render : function (_self)
34126 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34127 e.preventDefault();
34131 select : function (_self, oldvalue, newvalue)
34133 _this.setValue(_this.getValue());
34138 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34140 this.yearField = new Roo.bootstrap.ComboBox({
34141 allowBlank : this.yearAllowBlank,
34142 alwaysQuery : true,
34143 displayField : 'value',
34146 forceSelection : true,
34148 placeholder : this.yearPlaceholder,
34149 selectOnFocus : true,
34150 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34151 triggerAction : 'all',
34153 valueField : 'value',
34154 store : new Roo.data.SimpleStore({
34155 data : (function() {
34157 _this.fireEvent('years', _this, years);
34160 fields : [ 'value' ]
34163 select : function (_self, record, index)
34165 _this.setValue(_this.getValue());
34170 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34173 setValue : function(v, format)
34175 this.inputEl.dom.value = v;
34177 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34179 var d = Date.parseDate(v, f);
34186 this.setDay(d.format(this.dayFormat));
34187 this.setMonth(d.format(this.monthFormat));
34188 this.setYear(d.format(this.yearFormat));
34195 setDay : function(v)
34197 this.dayField.setValue(v);
34198 this.inputEl.dom.value = this.getValue();
34203 setMonth : function(v)
34205 this.monthField.setValue(v, true);
34206 this.inputEl.dom.value = this.getValue();
34211 setYear : function(v)
34213 this.yearField.setValue(v);
34214 this.inputEl.dom.value = this.getValue();
34219 getDay : function()
34221 return this.dayField.getValue();
34224 getMonth : function()
34226 return this.monthField.getValue();
34229 getYear : function()
34231 return this.yearField.getValue();
34234 getValue : function()
34236 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34238 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34248 this.inputEl.dom.value = '';
34253 validate : function()
34255 var d = this.dayField.validate();
34256 var m = this.monthField.validate();
34257 var y = this.yearField.validate();
34262 (!this.dayAllowBlank && !d) ||
34263 (!this.monthAllowBlank && !m) ||
34264 (!this.yearAllowBlank && !y)
34269 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34278 this.markInvalid();
34283 markValid : function()
34286 var label = this.el.select('label', true).first();
34287 var icon = this.el.select('i.fa-star', true).first();
34293 this.fireEvent('valid', this);
34297 * Mark this field as invalid
34298 * @param {String} msg The validation message
34300 markInvalid : function(msg)
34303 var label = this.el.select('label', true).first();
34304 var icon = this.el.select('i.fa-star', true).first();
34306 if(label && !icon){
34307 this.el.select('.roo-date-split-field-label', true).createChild({
34309 cls : 'text-danger fa fa-lg fa-star',
34310 tooltip : 'This field is required',
34311 style : 'margin-right:5px;'
34315 this.fireEvent('invalid', this, msg);
34318 clearInvalid : function()
34320 var label = this.el.select('label', true).first();
34321 var icon = this.el.select('i.fa-star', true).first();
34327 this.fireEvent('valid', this);
34330 getName: function()
34340 * http://masonry.desandro.com
34342 * The idea is to render all the bricks based on vertical width...
34344 * The original code extends 'outlayer' - we might need to use that....
34350 * @class Roo.bootstrap.LayoutMasonry
34351 * @extends Roo.bootstrap.Component
34352 * Bootstrap Layout Masonry class
34355 * Create a new Element
34356 * @param {Object} config The config object
34359 Roo.bootstrap.LayoutMasonry = function(config){
34361 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34365 Roo.bootstrap.LayoutMasonry.register(this);
34371 * Fire after layout the items
34372 * @param {Roo.bootstrap.LayoutMasonry} this
34373 * @param {Roo.EventObject} e
34380 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34383 * @cfg {Boolean} isLayoutInstant = no animation?
34385 isLayoutInstant : false, // needed?
34388 * @cfg {Number} boxWidth width of the columns
34393 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34398 * @cfg {Number} padWidth padding below box..
34403 * @cfg {Number} gutter gutter width..
34408 * @cfg {Number} maxCols maximum number of columns
34414 * @cfg {Boolean} isAutoInitial defalut true
34416 isAutoInitial : true,
34421 * @cfg {Boolean} isHorizontal defalut false
34423 isHorizontal : false,
34425 currentSize : null,
34431 bricks: null, //CompositeElement
34435 _isLayoutInited : false,
34437 // isAlternative : false, // only use for vertical layout...
34440 * @cfg {Number} alternativePadWidth padding below box..
34442 alternativePadWidth : 50,
34444 selectedBrick : [],
34446 getAutoCreate : function(){
34448 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34452 cls: 'blog-masonary-wrapper ' + this.cls,
34454 cls : 'mas-boxes masonary'
34461 getChildContainer: function( )
34463 if (this.boxesEl) {
34464 return this.boxesEl;
34467 this.boxesEl = this.el.select('.mas-boxes').first();
34469 return this.boxesEl;
34473 initEvents : function()
34477 if(this.isAutoInitial){
34478 Roo.log('hook children rendered');
34479 this.on('childrenrendered', function() {
34480 Roo.log('children rendered');
34486 initial : function()
34488 this.selectedBrick = [];
34490 this.currentSize = this.el.getBox(true);
34492 Roo.EventManager.onWindowResize(this.resize, this);
34494 if(!this.isAutoInitial){
34502 //this.layout.defer(500,this);
34506 resize : function()
34508 var cs = this.el.getBox(true);
34511 this.currentSize.width == cs.width &&
34512 this.currentSize.x == cs.x &&
34513 this.currentSize.height == cs.height &&
34514 this.currentSize.y == cs.y
34516 Roo.log("no change in with or X or Y");
34520 this.currentSize = cs;
34526 layout : function()
34528 this._resetLayout();
34530 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34532 this.layoutItems( isInstant );
34534 this._isLayoutInited = true;
34536 this.fireEvent('layout', this);
34540 _resetLayout : function()
34542 if(this.isHorizontal){
34543 this.horizontalMeasureColumns();
34547 this.verticalMeasureColumns();
34551 verticalMeasureColumns : function()
34553 this.getContainerWidth();
34555 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34556 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34560 var boxWidth = this.boxWidth + this.padWidth;
34562 if(this.containerWidth < this.boxWidth){
34563 boxWidth = this.containerWidth
34566 var containerWidth = this.containerWidth;
34568 var cols = Math.floor(containerWidth / boxWidth);
34570 this.cols = Math.max( cols, 1 );
34572 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34574 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34576 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34578 this.colWidth = boxWidth + avail - this.padWidth;
34580 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34581 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34584 horizontalMeasureColumns : function()
34586 this.getContainerWidth();
34588 var boxWidth = this.boxWidth;
34590 if(this.containerWidth < boxWidth){
34591 boxWidth = this.containerWidth;
34594 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34596 this.el.setHeight(boxWidth);
34600 getContainerWidth : function()
34602 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34605 layoutItems : function( isInstant )
34607 Roo.log(this.bricks);
34609 var items = Roo.apply([], this.bricks);
34611 if(this.isHorizontal){
34612 this._horizontalLayoutItems( items , isInstant );
34616 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34617 // this._verticalAlternativeLayoutItems( items , isInstant );
34621 this._verticalLayoutItems( items , isInstant );
34625 _verticalLayoutItems : function ( items , isInstant)
34627 if ( !items || !items.length ) {
34632 ['xs', 'xs', 'xs', 'tall'],
34633 ['xs', 'xs', 'tall'],
34634 ['xs', 'xs', 'sm'],
34635 ['xs', 'xs', 'xs'],
34641 ['sm', 'xs', 'xs'],
34645 ['tall', 'xs', 'xs', 'xs'],
34646 ['tall', 'xs', 'xs'],
34658 Roo.each(items, function(item, k){
34660 switch (item.size) {
34661 // these layouts take up a full box,
34672 boxes.push([item]);
34695 var filterPattern = function(box, length)
34703 var pattern = box.slice(0, length);
34707 Roo.each(pattern, function(i){
34708 format.push(i.size);
34711 Roo.each(standard, function(s){
34713 if(String(s) != String(format)){
34722 if(!match && length == 1){
34727 filterPattern(box, length - 1);
34731 queue.push(pattern);
34733 box = box.slice(length, box.length);
34735 filterPattern(box, 4);
34741 Roo.each(boxes, function(box, k){
34747 if(box.length == 1){
34752 filterPattern(box, 4);
34756 this._processVerticalLayoutQueue( queue, isInstant );
34760 // _verticalAlternativeLayoutItems : function( items , isInstant )
34762 // if ( !items || !items.length ) {
34766 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34770 _horizontalLayoutItems : function ( items , isInstant)
34772 if ( !items || !items.length || items.length < 3) {
34778 var eItems = items.slice(0, 3);
34780 items = items.slice(3, items.length);
34783 ['xs', 'xs', 'xs', 'wide'],
34784 ['xs', 'xs', 'wide'],
34785 ['xs', 'xs', 'sm'],
34786 ['xs', 'xs', 'xs'],
34792 ['sm', 'xs', 'xs'],
34796 ['wide', 'xs', 'xs', 'xs'],
34797 ['wide', 'xs', 'xs'],
34810 Roo.each(items, function(item, k){
34812 switch (item.size) {
34823 boxes.push([item]);
34847 var filterPattern = function(box, length)
34855 var pattern = box.slice(0, length);
34859 Roo.each(pattern, function(i){
34860 format.push(i.size);
34863 Roo.each(standard, function(s){
34865 if(String(s) != String(format)){
34874 if(!match && length == 1){
34879 filterPattern(box, length - 1);
34883 queue.push(pattern);
34885 box = box.slice(length, box.length);
34887 filterPattern(box, 4);
34893 Roo.each(boxes, function(box, k){
34899 if(box.length == 1){
34904 filterPattern(box, 4);
34911 var pos = this.el.getBox(true);
34915 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34917 var hit_end = false;
34919 Roo.each(queue, function(box){
34923 Roo.each(box, function(b){
34925 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34935 Roo.each(box, function(b){
34937 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34940 mx = Math.max(mx, b.x);
34944 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34948 Roo.each(box, function(b){
34950 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34964 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34967 /** Sets position of item in DOM
34968 * @param {Element} item
34969 * @param {Number} x - horizontal position
34970 * @param {Number} y - vertical position
34971 * @param {Boolean} isInstant - disables transitions
34973 _processVerticalLayoutQueue : function( queue, isInstant )
34975 var pos = this.el.getBox(true);
34980 for (var i = 0; i < this.cols; i++){
34984 Roo.each(queue, function(box, k){
34986 var col = k % this.cols;
34988 Roo.each(box, function(b,kk){
34990 b.el.position('absolute');
34992 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34993 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34995 if(b.size == 'md-left' || b.size == 'md-right'){
34996 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34997 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35000 b.el.setWidth(width);
35001 b.el.setHeight(height);
35003 b.el.select('iframe',true).setSize(width,height);
35007 for (var i = 0; i < this.cols; i++){
35009 if(maxY[i] < maxY[col]){
35014 col = Math.min(col, i);
35018 x = pos.x + col * (this.colWidth + this.padWidth);
35022 var positions = [];
35024 switch (box.length){
35026 positions = this.getVerticalOneBoxColPositions(x, y, box);
35029 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35032 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35035 positions = this.getVerticalFourBoxColPositions(x, y, box);
35041 Roo.each(box, function(b,kk){
35043 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35045 var sz = b.el.getSize();
35047 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35055 for (var i = 0; i < this.cols; i++){
35056 mY = Math.max(mY, maxY[i]);
35059 this.el.setHeight(mY - pos.y);
35063 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35065 // var pos = this.el.getBox(true);
35068 // var maxX = pos.right;
35070 // var maxHeight = 0;
35072 // Roo.each(items, function(item, k){
35076 // item.el.position('absolute');
35078 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35080 // item.el.setWidth(width);
35082 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35084 // item.el.setHeight(height);
35087 // item.el.setXY([x, y], isInstant ? false : true);
35089 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35092 // y = y + height + this.alternativePadWidth;
35094 // maxHeight = maxHeight + height + this.alternativePadWidth;
35098 // this.el.setHeight(maxHeight);
35102 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35104 var pos = this.el.getBox(true);
35109 var maxX = pos.right;
35111 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35113 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35115 Roo.each(queue, function(box, k){
35117 Roo.each(box, function(b, kk){
35119 b.el.position('absolute');
35121 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35122 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35124 if(b.size == 'md-left' || b.size == 'md-right'){
35125 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35126 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35129 b.el.setWidth(width);
35130 b.el.setHeight(height);
35138 var positions = [];
35140 switch (box.length){
35142 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35145 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35148 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35151 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35157 Roo.each(box, function(b,kk){
35159 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35161 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35169 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35171 Roo.each(eItems, function(b,k){
35173 b.size = (k == 0) ? 'sm' : 'xs';
35174 b.x = (k == 0) ? 2 : 1;
35175 b.y = (k == 0) ? 2 : 1;
35177 b.el.position('absolute');
35179 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35181 b.el.setWidth(width);
35183 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35185 b.el.setHeight(height);
35189 var positions = [];
35192 x : maxX - this.unitWidth * 2 - this.gutter,
35197 x : maxX - this.unitWidth,
35198 y : minY + (this.unitWidth + this.gutter) * 2
35202 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35206 Roo.each(eItems, function(b,k){
35208 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35214 getVerticalOneBoxColPositions : function(x, y, box)
35218 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35220 if(box[0].size == 'md-left'){
35224 if(box[0].size == 'md-right'){
35229 x : x + (this.unitWidth + this.gutter) * rand,
35236 getVerticalTwoBoxColPositions : function(x, y, box)
35240 if(box[0].size == 'xs'){
35244 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35248 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35262 x : x + (this.unitWidth + this.gutter) * 2,
35263 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35270 getVerticalThreeBoxColPositions : function(x, y, box)
35274 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35282 x : x + (this.unitWidth + this.gutter) * 1,
35287 x : x + (this.unitWidth + this.gutter) * 2,
35295 if(box[0].size == 'xs' && box[1].size == 'xs'){
35304 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35308 x : x + (this.unitWidth + this.gutter) * 1,
35322 x : x + (this.unitWidth + this.gutter) * 2,
35327 x : x + (this.unitWidth + this.gutter) * 2,
35328 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35335 getVerticalFourBoxColPositions : function(x, y, box)
35339 if(box[0].size == 'xs'){
35348 y : y + (this.unitHeight + this.gutter) * 1
35353 y : y + (this.unitHeight + this.gutter) * 2
35357 x : x + (this.unitWidth + this.gutter) * 1,
35371 x : x + (this.unitWidth + this.gutter) * 2,
35376 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35377 y : y + (this.unitHeight + this.gutter) * 1
35381 x : x + (this.unitWidth + this.gutter) * 2,
35382 y : y + (this.unitWidth + this.gutter) * 2
35389 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35393 if(box[0].size == 'md-left'){
35395 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35402 if(box[0].size == 'md-right'){
35404 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35405 y : minY + (this.unitWidth + this.gutter) * 1
35411 var rand = Math.floor(Math.random() * (4 - box[0].y));
35414 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35415 y : minY + (this.unitWidth + this.gutter) * rand
35422 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35426 if(box[0].size == 'xs'){
35429 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35434 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35435 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35443 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35448 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35449 y : minY + (this.unitWidth + this.gutter) * 2
35456 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35460 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35463 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35468 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35469 y : minY + (this.unitWidth + this.gutter) * 1
35473 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35474 y : minY + (this.unitWidth + this.gutter) * 2
35481 if(box[0].size == 'xs' && box[1].size == 'xs'){
35484 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35489 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35494 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35495 y : minY + (this.unitWidth + this.gutter) * 1
35503 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35508 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35509 y : minY + (this.unitWidth + this.gutter) * 2
35513 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35514 y : minY + (this.unitWidth + this.gutter) * 2
35521 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35525 if(box[0].size == 'xs'){
35528 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35533 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35543 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35544 y : minY + (this.unitWidth + this.gutter) * 1
35552 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35557 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35558 y : minY + (this.unitWidth + this.gutter) * 2
35562 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].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) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35568 y : minY + (this.unitWidth + this.gutter) * 2
35576 * remove a Masonry Brick
35577 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35579 removeBrick : function(brick_id)
35585 for (var i = 0; i<this.bricks.length; i++) {
35586 if (this.bricks[i].id == brick_id) {
35587 this.bricks.splice(i,1);
35588 this.el.dom.removeChild(Roo.get(brick_id).dom);
35595 * adds a Masonry Brick
35596 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35598 addBrick : function(cfg)
35600 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35601 //this.register(cn);
35602 cn.parentId = this.id;
35603 cn.render(this.el);
35608 * register a Masonry Brick
35609 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35612 register : function(brick)
35614 this.bricks.push(brick);
35615 brick.masonryId = this.id;
35619 * clear all the Masonry Brick
35621 clearAll : function()
35624 //this.getChildContainer().dom.innerHTML = "";
35625 this.el.dom.innerHTML = '';
35628 getSelected : function()
35630 if (!this.selectedBrick) {
35634 return this.selectedBrick;
35638 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35642 * register a Masonry Layout
35643 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35646 register : function(layout)
35648 this.groups[layout.id] = layout;
35651 * fetch a Masonry Layout based on the masonry layout ID
35652 * @param {string} the masonry layout to add
35653 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35656 get: function(layout_id) {
35657 if (typeof(this.groups[layout_id]) == 'undefined') {
35660 return this.groups[layout_id] ;
35672 * http://masonry.desandro.com
35674 * The idea is to render all the bricks based on vertical width...
35676 * The original code extends 'outlayer' - we might need to use that....
35682 * @class Roo.bootstrap.LayoutMasonryAuto
35683 * @extends Roo.bootstrap.Component
35684 * Bootstrap Layout Masonry class
35687 * Create a new Element
35688 * @param {Object} config The config object
35691 Roo.bootstrap.LayoutMasonryAuto = function(config){
35692 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35695 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35698 * @cfg {Boolean} isFitWidth - resize the width..
35700 isFitWidth : false, // options..
35702 * @cfg {Boolean} isOriginLeft = left align?
35704 isOriginLeft : true,
35706 * @cfg {Boolean} isOriginTop = top align?
35708 isOriginTop : false,
35710 * @cfg {Boolean} isLayoutInstant = no animation?
35712 isLayoutInstant : false, // needed?
35714 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35716 isResizingContainer : true,
35718 * @cfg {Number} columnWidth width of the columns
35724 * @cfg {Number} maxCols maximum number of columns
35729 * @cfg {Number} padHeight padding below box..
35735 * @cfg {Boolean} isAutoInitial defalut true
35738 isAutoInitial : true,
35744 initialColumnWidth : 0,
35745 currentSize : null,
35747 colYs : null, // array.
35754 bricks: null, //CompositeElement
35755 cols : 0, // array?
35756 // element : null, // wrapped now this.el
35757 _isLayoutInited : null,
35760 getAutoCreate : function(){
35764 cls: 'blog-masonary-wrapper ' + this.cls,
35766 cls : 'mas-boxes masonary'
35773 getChildContainer: function( )
35775 if (this.boxesEl) {
35776 return this.boxesEl;
35779 this.boxesEl = this.el.select('.mas-boxes').first();
35781 return this.boxesEl;
35785 initEvents : function()
35789 if(this.isAutoInitial){
35790 Roo.log('hook children rendered');
35791 this.on('childrenrendered', function() {
35792 Roo.log('children rendered');
35799 initial : function()
35801 this.reloadItems();
35803 this.currentSize = this.el.getBox(true);
35805 /// was window resize... - let's see if this works..
35806 Roo.EventManager.onWindowResize(this.resize, this);
35808 if(!this.isAutoInitial){
35813 this.layout.defer(500,this);
35816 reloadItems: function()
35818 this.bricks = this.el.select('.masonry-brick', true);
35820 this.bricks.each(function(b) {
35821 //Roo.log(b.getSize());
35822 if (!b.attr('originalwidth')) {
35823 b.attr('originalwidth', b.getSize().width);
35828 Roo.log(this.bricks.elements.length);
35831 resize : function()
35834 var cs = this.el.getBox(true);
35836 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35837 Roo.log("no change in with or X");
35840 this.currentSize = cs;
35844 layout : function()
35847 this._resetLayout();
35848 //this._manageStamps();
35850 // don't animate first layout
35851 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35852 this.layoutItems( isInstant );
35854 // flag for initalized
35855 this._isLayoutInited = true;
35858 layoutItems : function( isInstant )
35860 //var items = this._getItemsForLayout( this.items );
35861 // original code supports filtering layout items.. we just ignore it..
35863 this._layoutItems( this.bricks , isInstant );
35865 this._postLayout();
35867 _layoutItems : function ( items , isInstant)
35869 //this.fireEvent( 'layout', this, items );
35872 if ( !items || !items.elements.length ) {
35873 // no items, emit event with empty array
35878 items.each(function(item) {
35879 Roo.log("layout item");
35881 // get x/y object from method
35882 var position = this._getItemLayoutPosition( item );
35884 position.item = item;
35885 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35886 queue.push( position );
35889 this._processLayoutQueue( queue );
35891 /** Sets position of item in DOM
35892 * @param {Element} item
35893 * @param {Number} x - horizontal position
35894 * @param {Number} y - vertical position
35895 * @param {Boolean} isInstant - disables transitions
35897 _processLayoutQueue : function( queue )
35899 for ( var i=0, len = queue.length; i < len; i++ ) {
35900 var obj = queue[i];
35901 obj.item.position('absolute');
35902 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35908 * Any logic you want to do after each layout,
35909 * i.e. size the container
35911 _postLayout : function()
35913 this.resizeContainer();
35916 resizeContainer : function()
35918 if ( !this.isResizingContainer ) {
35921 var size = this._getContainerSize();
35923 this.el.setSize(size.width,size.height);
35924 this.boxesEl.setSize(size.width,size.height);
35930 _resetLayout : function()
35932 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35933 this.colWidth = this.el.getWidth();
35934 //this.gutter = this.el.getWidth();
35936 this.measureColumns();
35942 this.colYs.push( 0 );
35948 measureColumns : function()
35950 this.getContainerWidth();
35951 // if columnWidth is 0, default to outerWidth of first item
35952 if ( !this.columnWidth ) {
35953 var firstItem = this.bricks.first();
35954 Roo.log(firstItem);
35955 this.columnWidth = this.containerWidth;
35956 if (firstItem && firstItem.attr('originalwidth') ) {
35957 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35959 // columnWidth fall back to item of first element
35960 Roo.log("set column width?");
35961 this.initialColumnWidth = this.columnWidth ;
35963 // if first elem has no width, default to size of container
35968 if (this.initialColumnWidth) {
35969 this.columnWidth = this.initialColumnWidth;
35974 // column width is fixed at the top - however if container width get's smaller we should
35977 // this bit calcs how man columns..
35979 var columnWidth = this.columnWidth += this.gutter;
35981 // calculate columns
35982 var containerWidth = this.containerWidth + this.gutter;
35984 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35985 // fix rounding errors, typically with gutters
35986 var excess = columnWidth - containerWidth % columnWidth;
35989 // if overshoot is less than a pixel, round up, otherwise floor it
35990 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35991 cols = Math[ mathMethod ]( cols );
35992 this.cols = Math.max( cols, 1 );
35993 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35995 // padding positioning..
35996 var totalColWidth = this.cols * this.columnWidth;
35997 var padavail = this.containerWidth - totalColWidth;
35998 // so for 2 columns - we need 3 'pads'
36000 var padNeeded = (1+this.cols) * this.padWidth;
36002 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36004 this.columnWidth += padExtra
36005 //this.padWidth = Math.floor(padavail / ( this.cols));
36007 // adjust colum width so that padding is fixed??
36009 // we have 3 columns ... total = width * 3
36010 // we have X left over... that should be used by
36012 //if (this.expandC) {
36020 getContainerWidth : function()
36022 /* // container is parent if fit width
36023 var container = this.isFitWidth ? this.element.parentNode : this.element;
36024 // check that this.size and size are there
36025 // IE8 triggers resize on body size change, so they might not be
36027 var size = getSize( container ); //FIXME
36028 this.containerWidth = size && size.innerWidth; //FIXME
36031 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36035 _getItemLayoutPosition : function( item ) // what is item?
36037 // we resize the item to our columnWidth..
36039 item.setWidth(this.columnWidth);
36040 item.autoBoxAdjust = false;
36042 var sz = item.getSize();
36044 // how many columns does this brick span
36045 var remainder = this.containerWidth % this.columnWidth;
36047 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36048 // round if off by 1 pixel, otherwise use ceil
36049 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36050 colSpan = Math.min( colSpan, this.cols );
36052 // normally this should be '1' as we dont' currently allow multi width columns..
36054 var colGroup = this._getColGroup( colSpan );
36055 // get the minimum Y value from the columns
36056 var minimumY = Math.min.apply( Math, colGroup );
36057 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36059 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36061 // position the brick
36063 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36064 y: this.currentSize.y + minimumY + this.padHeight
36068 // apply setHeight to necessary columns
36069 var setHeight = minimumY + sz.height + this.padHeight;
36070 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36072 var setSpan = this.cols + 1 - colGroup.length;
36073 for ( var i = 0; i < setSpan; i++ ) {
36074 this.colYs[ shortColIndex + i ] = setHeight ;
36081 * @param {Number} colSpan - number of columns the element spans
36082 * @returns {Array} colGroup
36084 _getColGroup : function( colSpan )
36086 if ( colSpan < 2 ) {
36087 // if brick spans only one column, use all the column Ys
36092 // how many different places could this brick fit horizontally
36093 var groupCount = this.cols + 1 - colSpan;
36094 // for each group potential horizontal position
36095 for ( var i = 0; i < groupCount; i++ ) {
36096 // make an array of colY values for that one group
36097 var groupColYs = this.colYs.slice( i, i + colSpan );
36098 // and get the max value of the array
36099 colGroup[i] = Math.max.apply( Math, groupColYs );
36104 _manageStamp : function( stamp )
36106 var stampSize = stamp.getSize();
36107 var offset = stamp.getBox();
36108 // get the columns that this stamp affects
36109 var firstX = this.isOriginLeft ? offset.x : offset.right;
36110 var lastX = firstX + stampSize.width;
36111 var firstCol = Math.floor( firstX / this.columnWidth );
36112 firstCol = Math.max( 0, firstCol );
36114 var lastCol = Math.floor( lastX / this.columnWidth );
36115 // lastCol should not go over if multiple of columnWidth #425
36116 lastCol -= lastX % this.columnWidth ? 0 : 1;
36117 lastCol = Math.min( this.cols - 1, lastCol );
36119 // set colYs to bottom of the stamp
36120 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36123 for ( var i = firstCol; i <= lastCol; i++ ) {
36124 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36129 _getContainerSize : function()
36131 this.maxY = Math.max.apply( Math, this.colYs );
36136 if ( this.isFitWidth ) {
36137 size.width = this._getContainerFitWidth();
36143 _getContainerFitWidth : function()
36145 var unusedCols = 0;
36146 // count unused columns
36149 if ( this.colYs[i] !== 0 ) {
36154 // fit container to columns that have been used
36155 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36158 needsResizeLayout : function()
36160 var previousWidth = this.containerWidth;
36161 this.getContainerWidth();
36162 return previousWidth !== this.containerWidth;
36177 * @class Roo.bootstrap.MasonryBrick
36178 * @extends Roo.bootstrap.Component
36179 * Bootstrap MasonryBrick class
36182 * Create a new MasonryBrick
36183 * @param {Object} config The config object
36186 Roo.bootstrap.MasonryBrick = function(config){
36188 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36190 Roo.bootstrap.MasonryBrick.register(this);
36196 * When a MasonryBrick is clcik
36197 * @param {Roo.bootstrap.MasonryBrick} this
36198 * @param {Roo.EventObject} e
36204 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36207 * @cfg {String} title
36211 * @cfg {String} html
36215 * @cfg {String} bgimage
36219 * @cfg {String} videourl
36223 * @cfg {String} cls
36227 * @cfg {String} href
36231 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36236 * @cfg {String} placetitle (center|bottom)
36241 * @cfg {Boolean} isFitContainer defalut true
36243 isFitContainer : true,
36246 * @cfg {Boolean} preventDefault defalut false
36248 preventDefault : false,
36251 * @cfg {Boolean} inverse defalut false
36253 maskInverse : false,
36255 getAutoCreate : function()
36257 if(!this.isFitContainer){
36258 return this.getSplitAutoCreate();
36261 var cls = 'masonry-brick masonry-brick-full';
36263 if(this.href.length){
36264 cls += ' masonry-brick-link';
36267 if(this.bgimage.length){
36268 cls += ' masonry-brick-image';
36271 if(this.maskInverse){
36272 cls += ' mask-inverse';
36275 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36276 cls += ' enable-mask';
36280 cls += ' masonry-' + this.size + '-brick';
36283 if(this.placetitle.length){
36285 switch (this.placetitle) {
36287 cls += ' masonry-center-title';
36290 cls += ' masonry-bottom-title';
36297 if(!this.html.length && !this.bgimage.length){
36298 cls += ' masonry-center-title';
36301 if(!this.html.length && this.bgimage.length){
36302 cls += ' masonry-bottom-title';
36307 cls += ' ' + this.cls;
36311 tag: (this.href.length) ? 'a' : 'div',
36316 cls: 'masonry-brick-mask'
36320 cls: 'masonry-brick-paragraph',
36326 if(this.href.length){
36327 cfg.href = this.href;
36330 var cn = cfg.cn[1].cn;
36332 if(this.title.length){
36335 cls: 'masonry-brick-title',
36340 if(this.html.length){
36343 cls: 'masonry-brick-text',
36348 if (!this.title.length && !this.html.length) {
36349 cfg.cn[1].cls += ' hide';
36352 if(this.bgimage.length){
36355 cls: 'masonry-brick-image-view',
36360 if(this.videourl.length){
36361 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36362 // youtube support only?
36365 cls: 'masonry-brick-image-view',
36368 allowfullscreen : true
36376 getSplitAutoCreate : function()
36378 var cls = 'masonry-brick masonry-brick-split';
36380 if(this.href.length){
36381 cls += ' masonry-brick-link';
36384 if(this.bgimage.length){
36385 cls += ' masonry-brick-image';
36389 cls += ' masonry-' + this.size + '-brick';
36392 switch (this.placetitle) {
36394 cls += ' masonry-center-title';
36397 cls += ' masonry-bottom-title';
36400 if(!this.bgimage.length){
36401 cls += ' masonry-center-title';
36404 if(this.bgimage.length){
36405 cls += ' masonry-bottom-title';
36411 cls += ' ' + this.cls;
36415 tag: (this.href.length) ? 'a' : 'div',
36420 cls: 'masonry-brick-split-head',
36424 cls: 'masonry-brick-paragraph',
36431 cls: 'masonry-brick-split-body',
36437 if(this.href.length){
36438 cfg.href = this.href;
36441 if(this.title.length){
36442 cfg.cn[0].cn[0].cn.push({
36444 cls: 'masonry-brick-title',
36449 if(this.html.length){
36450 cfg.cn[1].cn.push({
36452 cls: 'masonry-brick-text',
36457 if(this.bgimage.length){
36458 cfg.cn[0].cn.push({
36460 cls: 'masonry-brick-image-view',
36465 if(this.videourl.length){
36466 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36467 // youtube support only?
36468 cfg.cn[0].cn.cn.push({
36470 cls: 'masonry-brick-image-view',
36473 allowfullscreen : true
36480 initEvents: function()
36482 switch (this.size) {
36515 this.el.on('touchstart', this.onTouchStart, this);
36516 this.el.on('touchmove', this.onTouchMove, this);
36517 this.el.on('touchend', this.onTouchEnd, this);
36518 this.el.on('contextmenu', this.onContextMenu, this);
36520 this.el.on('mouseenter' ,this.enter, this);
36521 this.el.on('mouseleave', this.leave, this);
36522 this.el.on('click', this.onClick, this);
36525 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36526 this.parent().bricks.push(this);
36531 onClick: function(e, el)
36533 var time = this.endTimer - this.startTimer;
36534 // Roo.log(e.preventDefault());
36537 e.preventDefault();
36542 if(!this.preventDefault){
36546 e.preventDefault();
36548 if (this.activeClass != '') {
36549 this.selectBrick();
36552 this.fireEvent('click', this, e);
36555 enter: function(e, el)
36557 e.preventDefault();
36559 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36563 if(this.bgimage.length && this.html.length){
36564 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36568 leave: function(e, el)
36570 e.preventDefault();
36572 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36576 if(this.bgimage.length && this.html.length){
36577 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36581 onTouchStart: function(e, el)
36583 // e.preventDefault();
36585 this.touchmoved = false;
36587 if(!this.isFitContainer){
36591 if(!this.bgimage.length || !this.html.length){
36595 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36597 this.timer = new Date().getTime();
36601 onTouchMove: function(e, el)
36603 this.touchmoved = true;
36606 onContextMenu : function(e,el)
36608 e.preventDefault();
36609 e.stopPropagation();
36613 onTouchEnd: function(e, el)
36615 // e.preventDefault();
36617 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36624 if(!this.bgimage.length || !this.html.length){
36626 if(this.href.length){
36627 window.location.href = this.href;
36633 if(!this.isFitContainer){
36637 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36639 window.location.href = this.href;
36642 //selection on single brick only
36643 selectBrick : function() {
36645 if (!this.parentId) {
36649 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36650 var index = m.selectedBrick.indexOf(this.id);
36653 m.selectedBrick.splice(index,1);
36654 this.el.removeClass(this.activeClass);
36658 for(var i = 0; i < m.selectedBrick.length; i++) {
36659 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36660 b.el.removeClass(b.activeClass);
36663 m.selectedBrick = [];
36665 m.selectedBrick.push(this.id);
36666 this.el.addClass(this.activeClass);
36670 isSelected : function(){
36671 return this.el.hasClass(this.activeClass);
36676 Roo.apply(Roo.bootstrap.MasonryBrick, {
36679 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36681 * register a Masonry Brick
36682 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36685 register : function(brick)
36687 //this.groups[brick.id] = brick;
36688 this.groups.add(brick.id, brick);
36691 * fetch a masonry brick based on the masonry brick ID
36692 * @param {string} the masonry brick to add
36693 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36696 get: function(brick_id)
36698 // if (typeof(this.groups[brick_id]) == 'undefined') {
36701 // return this.groups[brick_id] ;
36703 if(this.groups.key(brick_id)) {
36704 return this.groups.key(brick_id);
36722 * @class Roo.bootstrap.Brick
36723 * @extends Roo.bootstrap.Component
36724 * Bootstrap Brick class
36727 * Create a new Brick
36728 * @param {Object} config The config object
36731 Roo.bootstrap.Brick = function(config){
36732 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36738 * When a Brick is click
36739 * @param {Roo.bootstrap.Brick} this
36740 * @param {Roo.EventObject} e
36746 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36749 * @cfg {String} title
36753 * @cfg {String} html
36757 * @cfg {String} bgimage
36761 * @cfg {String} cls
36765 * @cfg {String} href
36769 * @cfg {String} video
36773 * @cfg {Boolean} square
36777 getAutoCreate : function()
36779 var cls = 'roo-brick';
36781 if(this.href.length){
36782 cls += ' roo-brick-link';
36785 if(this.bgimage.length){
36786 cls += ' roo-brick-image';
36789 if(!this.html.length && !this.bgimage.length){
36790 cls += ' roo-brick-center-title';
36793 if(!this.html.length && this.bgimage.length){
36794 cls += ' roo-brick-bottom-title';
36798 cls += ' ' + this.cls;
36802 tag: (this.href.length) ? 'a' : 'div',
36807 cls: 'roo-brick-paragraph',
36813 if(this.href.length){
36814 cfg.href = this.href;
36817 var cn = cfg.cn[0].cn;
36819 if(this.title.length){
36822 cls: 'roo-brick-title',
36827 if(this.html.length){
36830 cls: 'roo-brick-text',
36837 if(this.bgimage.length){
36840 cls: 'roo-brick-image-view',
36848 initEvents: function()
36850 if(this.title.length || this.html.length){
36851 this.el.on('mouseenter' ,this.enter, this);
36852 this.el.on('mouseleave', this.leave, this);
36855 Roo.EventManager.onWindowResize(this.resize, this);
36857 if(this.bgimage.length){
36858 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36859 this.imageEl.on('load', this.onImageLoad, this);
36866 onImageLoad : function()
36871 resize : function()
36873 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36875 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36877 if(this.bgimage.length){
36878 var image = this.el.select('.roo-brick-image-view', true).first();
36880 image.setWidth(paragraph.getWidth());
36883 image.setHeight(paragraph.getWidth());
36886 this.el.setHeight(image.getHeight());
36887 paragraph.setHeight(image.getHeight());
36893 enter: function(e, el)
36895 e.preventDefault();
36897 if(this.bgimage.length){
36898 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36899 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36903 leave: function(e, el)
36905 e.preventDefault();
36907 if(this.bgimage.length){
36908 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36909 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36924 * @class Roo.bootstrap.NumberField
36925 * @extends Roo.bootstrap.Input
36926 * Bootstrap NumberField class
36932 * Create a new NumberField
36933 * @param {Object} config The config object
36936 Roo.bootstrap.NumberField = function(config){
36937 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36940 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36943 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36945 allowDecimals : true,
36947 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36949 decimalSeparator : ".",
36951 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36953 decimalPrecision : 2,
36955 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36957 allowNegative : true,
36960 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36964 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36966 minValue : Number.NEGATIVE_INFINITY,
36968 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36970 maxValue : Number.MAX_VALUE,
36972 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36974 minText : "The minimum value for this field is {0}",
36976 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36978 maxText : "The maximum value for this field is {0}",
36980 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36981 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36983 nanText : "{0} is not a valid number",
36985 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36987 thousandsDelimiter : false,
36989 * @cfg {String} valueAlign alignment of value
36991 valueAlign : "left",
36993 getAutoCreate : function()
36995 var hiddenInput = {
36999 cls: 'hidden-number-input'
37003 hiddenInput.name = this.name;
37008 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37010 this.name = hiddenInput.name;
37012 if(cfg.cn.length > 0) {
37013 cfg.cn.push(hiddenInput);
37020 initEvents : function()
37022 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37024 var allowed = "0123456789";
37026 if(this.allowDecimals){
37027 allowed += this.decimalSeparator;
37030 if(this.allowNegative){
37034 if(this.thousandsDelimiter) {
37038 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37040 var keyPress = function(e){
37042 var k = e.getKey();
37044 var c = e.getCharCode();
37047 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37048 allowed.indexOf(String.fromCharCode(c)) === -1
37054 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37058 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37063 this.el.on("keypress", keyPress, this);
37066 validateValue : function(value)
37069 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37073 var num = this.parseValue(value);
37076 this.markInvalid(String.format(this.nanText, value));
37080 if(num < this.minValue){
37081 this.markInvalid(String.format(this.minText, this.minValue));
37085 if(num > this.maxValue){
37086 this.markInvalid(String.format(this.maxText, this.maxValue));
37093 getValue : function()
37095 var v = this.hiddenEl().getValue();
37097 return this.fixPrecision(this.parseValue(v));
37100 parseValue : function(value)
37102 if(this.thousandsDelimiter) {
37104 r = new RegExp(",", "g");
37105 value = value.replace(r, "");
37108 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37109 return isNaN(value) ? '' : value;
37112 fixPrecision : function(value)
37114 if(this.thousandsDelimiter) {
37116 r = new RegExp(",", "g");
37117 value = value.replace(r, "");
37120 var nan = isNaN(value);
37122 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37123 return nan ? '' : value;
37125 return parseFloat(value).toFixed(this.decimalPrecision);
37128 setValue : function(v)
37130 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37136 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37138 this.inputEl().dom.value = (v == '') ? '' :
37139 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37141 if(!this.allowZero && v === '0') {
37142 this.hiddenEl().dom.value = '';
37143 this.inputEl().dom.value = '';
37150 decimalPrecisionFcn : function(v)
37152 return Math.floor(v);
37155 beforeBlur : function()
37157 var v = this.parseValue(this.getRawValue());
37159 if(v || v === 0 || v === ''){
37164 hiddenEl : function()
37166 return this.el.select('input.hidden-number-input',true).first();
37178 * @class Roo.bootstrap.DocumentSlider
37179 * @extends Roo.bootstrap.Component
37180 * Bootstrap DocumentSlider class
37183 * Create a new DocumentViewer
37184 * @param {Object} config The config object
37187 Roo.bootstrap.DocumentSlider = function(config){
37188 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37195 * Fire after initEvent
37196 * @param {Roo.bootstrap.DocumentSlider} this
37201 * Fire after update
37202 * @param {Roo.bootstrap.DocumentSlider} this
37208 * @param {Roo.bootstrap.DocumentSlider} this
37214 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37220 getAutoCreate : function()
37224 cls : 'roo-document-slider',
37228 cls : 'roo-document-slider-header',
37232 cls : 'roo-document-slider-header-title'
37238 cls : 'roo-document-slider-body',
37242 cls : 'roo-document-slider-prev',
37246 cls : 'fa fa-chevron-left'
37252 cls : 'roo-document-slider-thumb',
37256 cls : 'roo-document-slider-image'
37262 cls : 'roo-document-slider-next',
37266 cls : 'fa fa-chevron-right'
37278 initEvents : function()
37280 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37281 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37283 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37284 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37286 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37287 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37289 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37290 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37292 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37293 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37295 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37296 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37298 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37299 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37301 this.thumbEl.on('click', this.onClick, this);
37303 this.prevIndicator.on('click', this.prev, this);
37305 this.nextIndicator.on('click', this.next, this);
37309 initial : function()
37311 if(this.files.length){
37312 this.indicator = 1;
37316 this.fireEvent('initial', this);
37319 update : function()
37321 this.imageEl.attr('src', this.files[this.indicator - 1]);
37323 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37325 this.prevIndicator.show();
37327 if(this.indicator == 1){
37328 this.prevIndicator.hide();
37331 this.nextIndicator.show();
37333 if(this.indicator == this.files.length){
37334 this.nextIndicator.hide();
37337 this.thumbEl.scrollTo('top');
37339 this.fireEvent('update', this);
37342 onClick : function(e)
37344 e.preventDefault();
37346 this.fireEvent('click', this);
37351 e.preventDefault();
37353 this.indicator = Math.max(1, this.indicator - 1);
37360 e.preventDefault();
37362 this.indicator = Math.min(this.files.length, this.indicator + 1);
37376 * @class Roo.bootstrap.RadioSet
37377 * @extends Roo.bootstrap.Input
37378 * Bootstrap RadioSet class
37379 * @cfg {String} indicatorpos (left|right) default left
37380 * @cfg {Boolean} inline (true|false) inline the element (default true)
37381 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37383 * Create a new RadioSet
37384 * @param {Object} config The config object
37387 Roo.bootstrap.RadioSet = function(config){
37389 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37393 Roo.bootstrap.RadioSet.register(this);
37398 * Fires when the element is checked or unchecked.
37399 * @param {Roo.bootstrap.RadioSet} this This radio
37400 * @param {Roo.bootstrap.Radio} item The checked item
37405 * Fires when the element is click.
37406 * @param {Roo.bootstrap.RadioSet} this This radio set
37407 * @param {Roo.bootstrap.Radio} item The checked item
37408 * @param {Roo.EventObject} e The event object
37415 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37423 indicatorpos : 'left',
37425 getAutoCreate : function()
37429 cls : 'roo-radio-set-label',
37433 html : this.fieldLabel
37437 if (Roo.bootstrap.version == 3) {
37440 if(this.indicatorpos == 'left'){
37443 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37444 tooltip : 'This field is required'
37449 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37450 tooltip : 'This field is required'
37456 cls : 'roo-radio-set-items'
37459 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37461 if (align === 'left' && this.fieldLabel.length) {
37464 cls : "roo-radio-set-right",
37470 if(this.labelWidth > 12){
37471 label.style = "width: " + this.labelWidth + 'px';
37474 if(this.labelWidth < 13 && this.labelmd == 0){
37475 this.labelmd = this.labelWidth;
37478 if(this.labellg > 0){
37479 label.cls += ' col-lg-' + this.labellg;
37480 items.cls += ' col-lg-' + (12 - this.labellg);
37483 if(this.labelmd > 0){
37484 label.cls += ' col-md-' + this.labelmd;
37485 items.cls += ' col-md-' + (12 - this.labelmd);
37488 if(this.labelsm > 0){
37489 label.cls += ' col-sm-' + this.labelsm;
37490 items.cls += ' col-sm-' + (12 - this.labelsm);
37493 if(this.labelxs > 0){
37494 label.cls += ' col-xs-' + this.labelxs;
37495 items.cls += ' col-xs-' + (12 - this.labelxs);
37501 cls : 'roo-radio-set',
37505 cls : 'roo-radio-set-input',
37508 value : this.value ? this.value : ''
37515 if(this.weight.length){
37516 cfg.cls += ' roo-radio-' + this.weight;
37520 cfg.cls += ' roo-radio-set-inline';
37524 ['xs','sm','md','lg'].map(function(size){
37525 if (settings[size]) {
37526 cfg.cls += ' col-' + size + '-' + settings[size];
37534 initEvents : function()
37536 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37537 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37539 if(!this.fieldLabel.length){
37540 this.labelEl.hide();
37543 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37544 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37546 this.indicator = this.indicatorEl();
37548 if(this.indicator){
37549 this.indicator.addClass('invisible');
37552 this.originalValue = this.getValue();
37556 inputEl: function ()
37558 return this.el.select('.roo-radio-set-input', true).first();
37561 getChildContainer : function()
37563 return this.itemsEl;
37566 register : function(item)
37568 this.radioes.push(item);
37572 validate : function()
37574 if(this.getVisibilityEl().hasClass('hidden')){
37580 Roo.each(this.radioes, function(i){
37589 if(this.allowBlank) {
37593 if(this.disabled || valid){
37598 this.markInvalid();
37603 markValid : function()
37605 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37606 this.indicatorEl().removeClass('visible');
37607 this.indicatorEl().addClass('invisible');
37611 if (Roo.bootstrap.version == 3) {
37612 this.el.removeClass([this.invalidClass, this.validClass]);
37613 this.el.addClass(this.validClass);
37615 this.el.removeClass(['is-invalid','is-valid']);
37616 this.el.addClass(['is-valid']);
37618 this.fireEvent('valid', this);
37621 markInvalid : function(msg)
37623 if(this.allowBlank || this.disabled){
37627 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37628 this.indicatorEl().removeClass('invisible');
37629 this.indicatorEl().addClass('visible');
37631 if (Roo.bootstrap.version == 3) {
37632 this.el.removeClass([this.invalidClass, this.validClass]);
37633 this.el.addClass(this.invalidClass);
37635 this.el.removeClass(['is-invalid','is-valid']);
37636 this.el.addClass(['is-invalid']);
37639 this.fireEvent('invalid', this, msg);
37643 setValue : function(v, suppressEvent)
37645 if(this.value === v){
37652 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37655 Roo.each(this.radioes, function(i){
37657 i.el.removeClass('checked');
37660 Roo.each(this.radioes, function(i){
37662 if(i.value === v || i.value.toString() === v.toString()){
37664 i.el.addClass('checked');
37666 if(suppressEvent !== true){
37667 this.fireEvent('check', this, i);
37678 clearInvalid : function(){
37680 if(!this.el || this.preventMark){
37684 this.el.removeClass([this.invalidClass]);
37686 this.fireEvent('valid', this);
37691 Roo.apply(Roo.bootstrap.RadioSet, {
37695 register : function(set)
37697 this.groups[set.name] = set;
37700 get: function(name)
37702 if (typeof(this.groups[name]) == 'undefined') {
37706 return this.groups[name] ;
37712 * Ext JS Library 1.1.1
37713 * Copyright(c) 2006-2007, Ext JS, LLC.
37715 * Originally Released Under LGPL - original licence link has changed is not relivant.
37718 * <script type="text/javascript">
37723 * @class Roo.bootstrap.SplitBar
37724 * @extends Roo.util.Observable
37725 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37729 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37730 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37731 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37732 split.minSize = 100;
37733 split.maxSize = 600;
37734 split.animate = true;
37735 split.on('moved', splitterMoved);
37738 * Create a new SplitBar
37739 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37740 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37741 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37742 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37743 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37744 position of the SplitBar).
37746 Roo.bootstrap.SplitBar = function(cfg){
37751 // dragElement : elm
37752 // resizingElement: el,
37754 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37755 // placement : Roo.bootstrap.SplitBar.LEFT ,
37756 // existingProxy ???
37759 this.el = Roo.get(cfg.dragElement, true);
37760 this.el.dom.unselectable = "on";
37762 this.resizingEl = Roo.get(cfg.resizingElement, true);
37766 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37767 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37770 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37773 * The minimum size of the resizing element. (Defaults to 0)
37779 * The maximum size of the resizing element. (Defaults to 2000)
37782 this.maxSize = 2000;
37785 * Whether to animate the transition to the new size
37788 this.animate = false;
37791 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37794 this.useShim = false;
37799 if(!cfg.existingProxy){
37801 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37803 this.proxy = Roo.get(cfg.existingProxy).dom;
37806 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37809 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37812 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37815 this.dragSpecs = {};
37818 * @private The adapter to use to positon and resize elements
37820 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37821 this.adapter.init(this);
37823 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37825 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37826 this.el.addClass("roo-splitbar-h");
37829 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37830 this.el.addClass("roo-splitbar-v");
37836 * Fires when the splitter is moved (alias for {@link #event-moved})
37837 * @param {Roo.bootstrap.SplitBar} this
37838 * @param {Number} newSize the new width or height
37843 * Fires when the splitter is moved
37844 * @param {Roo.bootstrap.SplitBar} this
37845 * @param {Number} newSize the new width or height
37849 * @event beforeresize
37850 * Fires before the splitter is dragged
37851 * @param {Roo.bootstrap.SplitBar} this
37853 "beforeresize" : true,
37855 "beforeapply" : true
37858 Roo.util.Observable.call(this);
37861 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37862 onStartProxyDrag : function(x, y){
37863 this.fireEvent("beforeresize", this);
37865 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37867 o.enableDisplayMode("block");
37868 // all splitbars share the same overlay
37869 Roo.bootstrap.SplitBar.prototype.overlay = o;
37871 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37872 this.overlay.show();
37873 Roo.get(this.proxy).setDisplayed("block");
37874 var size = this.adapter.getElementSize(this);
37875 this.activeMinSize = this.getMinimumSize();;
37876 this.activeMaxSize = this.getMaximumSize();;
37877 var c1 = size - this.activeMinSize;
37878 var c2 = Math.max(this.activeMaxSize - size, 0);
37879 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37880 this.dd.resetConstraints();
37881 this.dd.setXConstraint(
37882 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37883 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37885 this.dd.setYConstraint(0, 0);
37887 this.dd.resetConstraints();
37888 this.dd.setXConstraint(0, 0);
37889 this.dd.setYConstraint(
37890 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37891 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37894 this.dragSpecs.startSize = size;
37895 this.dragSpecs.startPoint = [x, y];
37896 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37900 * @private Called after the drag operation by the DDProxy
37902 onEndProxyDrag : function(e){
37903 Roo.get(this.proxy).setDisplayed(false);
37904 var endPoint = Roo.lib.Event.getXY(e);
37906 this.overlay.hide();
37909 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37910 newSize = this.dragSpecs.startSize +
37911 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37912 endPoint[0] - this.dragSpecs.startPoint[0] :
37913 this.dragSpecs.startPoint[0] - endPoint[0]
37916 newSize = this.dragSpecs.startSize +
37917 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37918 endPoint[1] - this.dragSpecs.startPoint[1] :
37919 this.dragSpecs.startPoint[1] - endPoint[1]
37922 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37923 if(newSize != this.dragSpecs.startSize){
37924 if(this.fireEvent('beforeapply', this, newSize) !== false){
37925 this.adapter.setElementSize(this, newSize);
37926 this.fireEvent("moved", this, newSize);
37927 this.fireEvent("resize", this, newSize);
37933 * Get the adapter this SplitBar uses
37934 * @return The adapter object
37936 getAdapter : function(){
37937 return this.adapter;
37941 * Set the adapter this SplitBar uses
37942 * @param {Object} adapter A SplitBar adapter object
37944 setAdapter : function(adapter){
37945 this.adapter = adapter;
37946 this.adapter.init(this);
37950 * Gets the minimum size for the resizing element
37951 * @return {Number} The minimum size
37953 getMinimumSize : function(){
37954 return this.minSize;
37958 * Sets the minimum size for the resizing element
37959 * @param {Number} minSize The minimum size
37961 setMinimumSize : function(minSize){
37962 this.minSize = minSize;
37966 * Gets the maximum size for the resizing element
37967 * @return {Number} The maximum size
37969 getMaximumSize : function(){
37970 return this.maxSize;
37974 * Sets the maximum size for the resizing element
37975 * @param {Number} maxSize The maximum size
37977 setMaximumSize : function(maxSize){
37978 this.maxSize = maxSize;
37982 * Sets the initialize size for the resizing element
37983 * @param {Number} size The initial size
37985 setCurrentSize : function(size){
37986 var oldAnimate = this.animate;
37987 this.animate = false;
37988 this.adapter.setElementSize(this, size);
37989 this.animate = oldAnimate;
37993 * Destroy this splitbar.
37994 * @param {Boolean} removeEl True to remove the element
37996 destroy : function(removeEl){
37998 this.shim.remove();
38001 this.proxy.parentNode.removeChild(this.proxy);
38009 * @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.
38011 Roo.bootstrap.SplitBar.createProxy = function(dir){
38012 var proxy = new Roo.Element(document.createElement("div"));
38013 proxy.unselectable();
38014 var cls = 'roo-splitbar-proxy';
38015 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38016 document.body.appendChild(proxy.dom);
38021 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38022 * Default Adapter. It assumes the splitter and resizing element are not positioned
38023 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38025 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38028 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38029 // do nothing for now
38030 init : function(s){
38034 * Called before drag operations to get the current size of the resizing element.
38035 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38037 getElementSize : function(s){
38038 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38039 return s.resizingEl.getWidth();
38041 return s.resizingEl.getHeight();
38046 * Called after drag operations to set the size of the resizing element.
38047 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38048 * @param {Number} newSize The new size to set
38049 * @param {Function} onComplete A function to be invoked when resizing is complete
38051 setElementSize : function(s, newSize, onComplete){
38052 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38054 s.resizingEl.setWidth(newSize);
38056 onComplete(s, newSize);
38059 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38064 s.resizingEl.setHeight(newSize);
38066 onComplete(s, newSize);
38069 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38076 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38077 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38078 * Adapter that moves the splitter element to align with the resized sizing element.
38079 * Used with an absolute positioned SplitBar.
38080 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38081 * document.body, make sure you assign an id to the body element.
38083 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38084 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38085 this.container = Roo.get(container);
38088 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38089 init : function(s){
38090 this.basic.init(s);
38093 getElementSize : function(s){
38094 return this.basic.getElementSize(s);
38097 setElementSize : function(s, newSize, onComplete){
38098 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38101 moveSplitter : function(s){
38102 var yes = Roo.bootstrap.SplitBar;
38103 switch(s.placement){
38105 s.el.setX(s.resizingEl.getRight());
38108 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38111 s.el.setY(s.resizingEl.getBottom());
38114 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38121 * Orientation constant - Create a vertical SplitBar
38125 Roo.bootstrap.SplitBar.VERTICAL = 1;
38128 * Orientation constant - Create a horizontal SplitBar
38132 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38135 * Placement constant - The resizing element is to the left of the splitter element
38139 Roo.bootstrap.SplitBar.LEFT = 1;
38142 * Placement constant - The resizing element is to the right of the splitter element
38146 Roo.bootstrap.SplitBar.RIGHT = 2;
38149 * Placement constant - The resizing element is positioned above the splitter element
38153 Roo.bootstrap.SplitBar.TOP = 3;
38156 * Placement constant - The resizing element is positioned under splitter element
38160 Roo.bootstrap.SplitBar.BOTTOM = 4;
38161 Roo.namespace("Roo.bootstrap.layout");/*
38163 * Ext JS Library 1.1.1
38164 * Copyright(c) 2006-2007, Ext JS, LLC.
38166 * Originally Released Under LGPL - original licence link has changed is not relivant.
38169 * <script type="text/javascript">
38173 * @class Roo.bootstrap.layout.Manager
38174 * @extends Roo.bootstrap.Component
38175 * Base class for layout managers.
38177 Roo.bootstrap.layout.Manager = function(config)
38179 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38185 /** false to disable window resize monitoring @type Boolean */
38186 this.monitorWindowResize = true;
38191 * Fires when a layout is performed.
38192 * @param {Roo.LayoutManager} this
38196 * @event regionresized
38197 * Fires when the user resizes a region.
38198 * @param {Roo.LayoutRegion} region The resized region
38199 * @param {Number} newSize The new size (width for east/west, height for north/south)
38201 "regionresized" : true,
38203 * @event regioncollapsed
38204 * Fires when a region is collapsed.
38205 * @param {Roo.LayoutRegion} region The collapsed region
38207 "regioncollapsed" : true,
38209 * @event regionexpanded
38210 * Fires when a region is expanded.
38211 * @param {Roo.LayoutRegion} region The expanded region
38213 "regionexpanded" : true
38215 this.updating = false;
38218 this.el = Roo.get(config.el);
38224 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38229 monitorWindowResize : true,
38235 onRender : function(ct, position)
38238 this.el = Roo.get(ct);
38241 //this.fireEvent('render',this);
38245 initEvents: function()
38249 // ie scrollbar fix
38250 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38251 document.body.scroll = "no";
38252 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38253 this.el.position('relative');
38255 this.id = this.el.id;
38256 this.el.addClass("roo-layout-container");
38257 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38258 if(this.el.dom != document.body ) {
38259 this.el.on('resize', this.layout,this);
38260 this.el.on('show', this.layout,this);
38266 * Returns true if this layout is currently being updated
38267 * @return {Boolean}
38269 isUpdating : function(){
38270 return this.updating;
38274 * Suspend the LayoutManager from doing auto-layouts while
38275 * making multiple add or remove calls
38277 beginUpdate : function(){
38278 this.updating = true;
38282 * Restore auto-layouts and optionally disable the manager from performing a layout
38283 * @param {Boolean} noLayout true to disable a layout update
38285 endUpdate : function(noLayout){
38286 this.updating = false;
38292 layout: function(){
38296 onRegionResized : function(region, newSize){
38297 this.fireEvent("regionresized", region, newSize);
38301 onRegionCollapsed : function(region){
38302 this.fireEvent("regioncollapsed", region);
38305 onRegionExpanded : function(region){
38306 this.fireEvent("regionexpanded", region);
38310 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38311 * performs box-model adjustments.
38312 * @return {Object} The size as an object {width: (the width), height: (the height)}
38314 getViewSize : function()
38317 if(this.el.dom != document.body){
38318 size = this.el.getSize();
38320 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38322 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38323 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38328 * Returns the Element this layout is bound to.
38329 * @return {Roo.Element}
38331 getEl : function(){
38336 * Returns the specified region.
38337 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38338 * @return {Roo.LayoutRegion}
38340 getRegion : function(target){
38341 return this.regions[target.toLowerCase()];
38344 onWindowResize : function(){
38345 if(this.monitorWindowResize){
38352 * Ext JS Library 1.1.1
38353 * Copyright(c) 2006-2007, Ext JS, LLC.
38355 * Originally Released Under LGPL - original licence link has changed is not relivant.
38358 * <script type="text/javascript">
38361 * @class Roo.bootstrap.layout.Border
38362 * @extends Roo.bootstrap.layout.Manager
38363 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38364 * please see: examples/bootstrap/nested.html<br><br>
38366 <b>The container the layout is rendered into can be either the body element or any other element.
38367 If it is not the body element, the container needs to either be an absolute positioned element,
38368 or you will need to add "position:relative" to the css of the container. You will also need to specify
38369 the container size if it is not the body element.</b>
38372 * Create a new Border
38373 * @param {Object} config Configuration options
38375 Roo.bootstrap.layout.Border = function(config){
38376 config = config || {};
38377 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38381 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38382 if(config[region]){
38383 config[region].region = region;
38384 this.addRegion(config[region]);
38390 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38392 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38394 parent : false, // this might point to a 'nest' or a ???
38397 * Creates and adds a new region if it doesn't already exist.
38398 * @param {String} target The target region key (north, south, east, west or center).
38399 * @param {Object} config The regions config object
38400 * @return {BorderLayoutRegion} The new region
38402 addRegion : function(config)
38404 if(!this.regions[config.region]){
38405 var r = this.factory(config);
38406 this.bindRegion(r);
38408 return this.regions[config.region];
38412 bindRegion : function(r){
38413 this.regions[r.config.region] = r;
38415 r.on("visibilitychange", this.layout, this);
38416 r.on("paneladded", this.layout, this);
38417 r.on("panelremoved", this.layout, this);
38418 r.on("invalidated", this.layout, this);
38419 r.on("resized", this.onRegionResized, this);
38420 r.on("collapsed", this.onRegionCollapsed, this);
38421 r.on("expanded", this.onRegionExpanded, this);
38425 * Performs a layout update.
38427 layout : function()
38429 if(this.updating) {
38433 // render all the rebions if they have not been done alreayd?
38434 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38435 if(this.regions[region] && !this.regions[region].bodyEl){
38436 this.regions[region].onRender(this.el)
38440 var size = this.getViewSize();
38441 var w = size.width;
38442 var h = size.height;
38447 //var x = 0, y = 0;
38449 var rs = this.regions;
38450 var north = rs["north"];
38451 var south = rs["south"];
38452 var west = rs["west"];
38453 var east = rs["east"];
38454 var center = rs["center"];
38455 //if(this.hideOnLayout){ // not supported anymore
38456 //c.el.setStyle("display", "none");
38458 if(north && north.isVisible()){
38459 var b = north.getBox();
38460 var m = north.getMargins();
38461 b.width = w - (m.left+m.right);
38464 centerY = b.height + b.y + m.bottom;
38465 centerH -= centerY;
38466 north.updateBox(this.safeBox(b));
38468 if(south && south.isVisible()){
38469 var b = south.getBox();
38470 var m = south.getMargins();
38471 b.width = w - (m.left+m.right);
38473 var totalHeight = (b.height + m.top + m.bottom);
38474 b.y = h - totalHeight + m.top;
38475 centerH -= totalHeight;
38476 south.updateBox(this.safeBox(b));
38478 if(west && west.isVisible()){
38479 var b = west.getBox();
38480 var m = west.getMargins();
38481 b.height = centerH - (m.top+m.bottom);
38483 b.y = centerY + m.top;
38484 var totalWidth = (b.width + m.left + m.right);
38485 centerX += totalWidth;
38486 centerW -= totalWidth;
38487 west.updateBox(this.safeBox(b));
38489 if(east && east.isVisible()){
38490 var b = east.getBox();
38491 var m = east.getMargins();
38492 b.height = centerH - (m.top+m.bottom);
38493 var totalWidth = (b.width + m.left + m.right);
38494 b.x = w - totalWidth + m.left;
38495 b.y = centerY + m.top;
38496 centerW -= totalWidth;
38497 east.updateBox(this.safeBox(b));
38500 var m = center.getMargins();
38502 x: centerX + m.left,
38503 y: centerY + m.top,
38504 width: centerW - (m.left+m.right),
38505 height: centerH - (m.top+m.bottom)
38507 //if(this.hideOnLayout){
38508 //center.el.setStyle("display", "block");
38510 center.updateBox(this.safeBox(centerBox));
38513 this.fireEvent("layout", this);
38517 safeBox : function(box){
38518 box.width = Math.max(0, box.width);
38519 box.height = Math.max(0, box.height);
38524 * Adds a ContentPanel (or subclass) to this layout.
38525 * @param {String} target The target region key (north, south, east, west or center).
38526 * @param {Roo.ContentPanel} panel The panel to add
38527 * @return {Roo.ContentPanel} The added panel
38529 add : function(target, panel){
38531 target = target.toLowerCase();
38532 return this.regions[target].add(panel);
38536 * Remove a ContentPanel (or subclass) to this layout.
38537 * @param {String} target The target region key (north, south, east, west or center).
38538 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38539 * @return {Roo.ContentPanel} The removed panel
38541 remove : function(target, panel){
38542 target = target.toLowerCase();
38543 return this.regions[target].remove(panel);
38547 * Searches all regions for a panel with the specified id
38548 * @param {String} panelId
38549 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38551 findPanel : function(panelId){
38552 var rs = this.regions;
38553 for(var target in rs){
38554 if(typeof rs[target] != "function"){
38555 var p = rs[target].getPanel(panelId);
38565 * Searches all regions for a panel with the specified id and activates (shows) it.
38566 * @param {String/ContentPanel} panelId The panels id or the panel itself
38567 * @return {Roo.ContentPanel} The shown panel or null
38569 showPanel : function(panelId) {
38570 var rs = this.regions;
38571 for(var target in rs){
38572 var r = rs[target];
38573 if(typeof r != "function"){
38574 if(r.hasPanel(panelId)){
38575 return r.showPanel(panelId);
38583 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38584 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38587 restoreState : function(provider){
38589 provider = Roo.state.Manager;
38591 var sm = new Roo.LayoutStateManager();
38592 sm.init(this, provider);
38598 * Adds a xtype elements to the layout.
38602 xtype : 'ContentPanel',
38609 xtype : 'NestedLayoutPanel',
38615 items : [ ... list of content panels or nested layout panels.. ]
38619 * @param {Object} cfg Xtype definition of item to add.
38621 addxtype : function(cfg)
38623 // basically accepts a pannel...
38624 // can accept a layout region..!?!?
38625 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38628 // theory? children can only be panels??
38630 //if (!cfg.xtype.match(/Panel$/)) {
38635 if (typeof(cfg.region) == 'undefined') {
38636 Roo.log("Failed to add Panel, region was not set");
38640 var region = cfg.region;
38646 xitems = cfg.items;
38651 if ( region == 'center') {
38652 Roo.log("Center: " + cfg.title);
38658 case 'Content': // ContentPanel (el, cfg)
38659 case 'Scroll': // ContentPanel (el, cfg)
38661 cfg.autoCreate = cfg.autoCreate || true;
38662 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38664 // var el = this.el.createChild();
38665 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38668 this.add(region, ret);
38672 case 'TreePanel': // our new panel!
38673 cfg.el = this.el.createChild();
38674 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38675 this.add(region, ret);
38680 // create a new Layout (which is a Border Layout...
38682 var clayout = cfg.layout;
38683 clayout.el = this.el.createChild();
38684 clayout.items = clayout.items || [];
38688 // replace this exitems with the clayout ones..
38689 xitems = clayout.items;
38691 // force background off if it's in center...
38692 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38693 cfg.background = false;
38695 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38698 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38699 //console.log('adding nested layout panel ' + cfg.toSource());
38700 this.add(region, ret);
38701 nb = {}; /// find first...
38706 // needs grid and region
38708 //var el = this.getRegion(region).el.createChild();
38710 *var el = this.el.createChild();
38711 // create the grid first...
38712 cfg.grid.container = el;
38713 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38716 if (region == 'center' && this.active ) {
38717 cfg.background = false;
38720 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38722 this.add(region, ret);
38724 if (cfg.background) {
38725 // render grid on panel activation (if panel background)
38726 ret.on('activate', function(gp) {
38727 if (!gp.grid.rendered) {
38728 // gp.grid.render(el);
38732 // cfg.grid.render(el);
38738 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38739 // it was the old xcomponent building that caused this before.
38740 // espeically if border is the top element in the tree.
38750 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38752 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38753 this.add(region, ret);
38757 throw "Can not add '" + cfg.xtype + "' to Border";
38763 this.beginUpdate();
38767 Roo.each(xitems, function(i) {
38768 region = nb && i.region ? i.region : false;
38770 var add = ret.addxtype(i);
38773 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38774 if (!i.background) {
38775 abn[region] = nb[region] ;
38782 // make the last non-background panel active..
38783 //if (nb) { Roo.log(abn); }
38786 for(var r in abn) {
38787 region = this.getRegion(r);
38789 // tried using nb[r], but it does not work..
38791 region.showPanel(abn[r]);
38802 factory : function(cfg)
38805 var validRegions = Roo.bootstrap.layout.Border.regions;
38807 var target = cfg.region;
38810 var r = Roo.bootstrap.layout;
38814 return new r.North(cfg);
38816 return new r.South(cfg);
38818 return new r.East(cfg);
38820 return new r.West(cfg);
38822 return new r.Center(cfg);
38824 throw 'Layout region "'+target+'" not supported.';
38831 * Ext JS Library 1.1.1
38832 * Copyright(c) 2006-2007, Ext JS, LLC.
38834 * Originally Released Under LGPL - original licence link has changed is not relivant.
38837 * <script type="text/javascript">
38841 * @class Roo.bootstrap.layout.Basic
38842 * @extends Roo.util.Observable
38843 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38844 * and does not have a titlebar, tabs or any other features. All it does is size and position
38845 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38846 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38847 * @cfg {string} region the region that it inhabits..
38848 * @cfg {bool} skipConfig skip config?
38852 Roo.bootstrap.layout.Basic = function(config){
38854 this.mgr = config.mgr;
38856 this.position = config.region;
38858 var skipConfig = config.skipConfig;
38862 * @scope Roo.BasicLayoutRegion
38866 * @event beforeremove
38867 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38868 * @param {Roo.LayoutRegion} this
38869 * @param {Roo.ContentPanel} panel The panel
38870 * @param {Object} e The cancel event object
38872 "beforeremove" : true,
38874 * @event invalidated
38875 * Fires when the layout for this region is changed.
38876 * @param {Roo.LayoutRegion} this
38878 "invalidated" : true,
38880 * @event visibilitychange
38881 * Fires when this region is shown or hidden
38882 * @param {Roo.LayoutRegion} this
38883 * @param {Boolean} visibility true or false
38885 "visibilitychange" : true,
38887 * @event paneladded
38888 * Fires when a panel is added.
38889 * @param {Roo.LayoutRegion} this
38890 * @param {Roo.ContentPanel} panel The panel
38892 "paneladded" : true,
38894 * @event panelremoved
38895 * Fires when a panel is removed.
38896 * @param {Roo.LayoutRegion} this
38897 * @param {Roo.ContentPanel} panel The panel
38899 "panelremoved" : true,
38901 * @event beforecollapse
38902 * Fires when this region before collapse.
38903 * @param {Roo.LayoutRegion} this
38905 "beforecollapse" : true,
38908 * Fires when this region is collapsed.
38909 * @param {Roo.LayoutRegion} this
38911 "collapsed" : true,
38914 * Fires when this region is expanded.
38915 * @param {Roo.LayoutRegion} this
38920 * Fires when this region is slid into view.
38921 * @param {Roo.LayoutRegion} this
38923 "slideshow" : true,
38926 * Fires when this region slides out of view.
38927 * @param {Roo.LayoutRegion} this
38929 "slidehide" : true,
38931 * @event panelactivated
38932 * Fires when a panel is activated.
38933 * @param {Roo.LayoutRegion} this
38934 * @param {Roo.ContentPanel} panel The activated panel
38936 "panelactivated" : true,
38939 * Fires when the user resizes this region.
38940 * @param {Roo.LayoutRegion} this
38941 * @param {Number} newSize The new size (width for east/west, height for north/south)
38945 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38946 this.panels = new Roo.util.MixedCollection();
38947 this.panels.getKey = this.getPanelId.createDelegate(this);
38949 this.activePanel = null;
38950 // ensure listeners are added...
38952 if (config.listeners || config.events) {
38953 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38954 listeners : config.listeners || {},
38955 events : config.events || {}
38959 if(skipConfig !== true){
38960 this.applyConfig(config);
38964 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38966 getPanelId : function(p){
38970 applyConfig : function(config){
38971 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38972 this.config = config;
38977 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38978 * the width, for horizontal (north, south) the height.
38979 * @param {Number} newSize The new width or height
38981 resizeTo : function(newSize){
38982 var el = this.el ? this.el :
38983 (this.activePanel ? this.activePanel.getEl() : null);
38985 switch(this.position){
38988 el.setWidth(newSize);
38989 this.fireEvent("resized", this, newSize);
38993 el.setHeight(newSize);
38994 this.fireEvent("resized", this, newSize);
39000 getBox : function(){
39001 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39004 getMargins : function(){
39005 return this.margins;
39008 updateBox : function(box){
39010 var el = this.activePanel.getEl();
39011 el.dom.style.left = box.x + "px";
39012 el.dom.style.top = box.y + "px";
39013 this.activePanel.setSize(box.width, box.height);
39017 * Returns the container element for this region.
39018 * @return {Roo.Element}
39020 getEl : function(){
39021 return this.activePanel;
39025 * Returns true if this region is currently visible.
39026 * @return {Boolean}
39028 isVisible : function(){
39029 return this.activePanel ? true : false;
39032 setActivePanel : function(panel){
39033 panel = this.getPanel(panel);
39034 if(this.activePanel && this.activePanel != panel){
39035 this.activePanel.setActiveState(false);
39036 this.activePanel.getEl().setLeftTop(-10000,-10000);
39038 this.activePanel = panel;
39039 panel.setActiveState(true);
39041 panel.setSize(this.box.width, this.box.height);
39043 this.fireEvent("panelactivated", this, panel);
39044 this.fireEvent("invalidated");
39048 * Show the specified panel.
39049 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39050 * @return {Roo.ContentPanel} The shown panel or null
39052 showPanel : function(panel){
39053 panel = this.getPanel(panel);
39055 this.setActivePanel(panel);
39061 * Get the active panel for this region.
39062 * @return {Roo.ContentPanel} The active panel or null
39064 getActivePanel : function(){
39065 return this.activePanel;
39069 * Add the passed ContentPanel(s)
39070 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39071 * @return {Roo.ContentPanel} The panel added (if only one was added)
39073 add : function(panel){
39074 if(arguments.length > 1){
39075 for(var i = 0, len = arguments.length; i < len; i++) {
39076 this.add(arguments[i]);
39080 if(this.hasPanel(panel)){
39081 this.showPanel(panel);
39084 var el = panel.getEl();
39085 if(el.dom.parentNode != this.mgr.el.dom){
39086 this.mgr.el.dom.appendChild(el.dom);
39088 if(panel.setRegion){
39089 panel.setRegion(this);
39091 this.panels.add(panel);
39092 el.setStyle("position", "absolute");
39093 if(!panel.background){
39094 this.setActivePanel(panel);
39095 if(this.config.initialSize && this.panels.getCount()==1){
39096 this.resizeTo(this.config.initialSize);
39099 this.fireEvent("paneladded", this, panel);
39104 * Returns true if the panel is in this region.
39105 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39106 * @return {Boolean}
39108 hasPanel : function(panel){
39109 if(typeof panel == "object"){ // must be panel obj
39110 panel = panel.getId();
39112 return this.getPanel(panel) ? true : false;
39116 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39117 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39118 * @param {Boolean} preservePanel Overrides the config preservePanel option
39119 * @return {Roo.ContentPanel} The panel that was removed
39121 remove : function(panel, preservePanel){
39122 panel = this.getPanel(panel);
39127 this.fireEvent("beforeremove", this, panel, e);
39128 if(e.cancel === true){
39131 var panelId = panel.getId();
39132 this.panels.removeKey(panelId);
39137 * Returns the panel specified or null if it's not in this region.
39138 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39139 * @return {Roo.ContentPanel}
39141 getPanel : function(id){
39142 if(typeof id == "object"){ // must be panel obj
39145 return this.panels.get(id);
39149 * Returns this regions position (north/south/east/west/center).
39152 getPosition: function(){
39153 return this.position;
39157 * Ext JS Library 1.1.1
39158 * Copyright(c) 2006-2007, Ext JS, LLC.
39160 * Originally Released Under LGPL - original licence link has changed is not relivant.
39163 * <script type="text/javascript">
39167 * @class Roo.bootstrap.layout.Region
39168 * @extends Roo.bootstrap.layout.Basic
39169 * This class represents a region in a layout manager.
39171 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39172 * @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})
39173 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39174 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39175 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39176 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39177 * @cfg {String} title The title for the region (overrides panel titles)
39178 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39179 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39180 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39181 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39182 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39183 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39184 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39185 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39186 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39187 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39189 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39190 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39191 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39192 * @cfg {Number} width For East/West panels
39193 * @cfg {Number} height For North/South panels
39194 * @cfg {Boolean} split To show the splitter
39195 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39197 * @cfg {string} cls Extra CSS classes to add to region
39199 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39200 * @cfg {string} region the region that it inhabits..
39203 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39204 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39206 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39207 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39208 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39210 Roo.bootstrap.layout.Region = function(config)
39212 this.applyConfig(config);
39214 var mgr = config.mgr;
39215 var pos = config.region;
39216 config.skipConfig = true;
39217 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39220 this.onRender(mgr.el);
39223 this.visible = true;
39224 this.collapsed = false;
39225 this.unrendered_panels = [];
39228 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39230 position: '', // set by wrapper (eg. north/south etc..)
39231 unrendered_panels : null, // unrendered panels.
39233 tabPosition : false,
39235 mgr: false, // points to 'Border'
39238 createBody : function(){
39239 /** This region's body element
39240 * @type Roo.Element */
39241 this.bodyEl = this.el.createChild({
39243 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39247 onRender: function(ctr, pos)
39249 var dh = Roo.DomHelper;
39250 /** This region's container element
39251 * @type Roo.Element */
39252 this.el = dh.append(ctr.dom, {
39254 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39256 /** This region's title element
39257 * @type Roo.Element */
39259 this.titleEl = dh.append(this.el.dom, {
39261 unselectable: "on",
39262 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39264 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39265 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39269 this.titleEl.enableDisplayMode();
39270 /** This region's title text element
39271 * @type HTMLElement */
39272 this.titleTextEl = this.titleEl.dom.firstChild;
39273 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39275 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39276 this.closeBtn.enableDisplayMode();
39277 this.closeBtn.on("click", this.closeClicked, this);
39278 this.closeBtn.hide();
39280 this.createBody(this.config);
39281 if(this.config.hideWhenEmpty){
39283 this.on("paneladded", this.validateVisibility, this);
39284 this.on("panelremoved", this.validateVisibility, this);
39286 if(this.autoScroll){
39287 this.bodyEl.setStyle("overflow", "auto");
39289 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39291 //if(c.titlebar !== false){
39292 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39293 this.titleEl.hide();
39295 this.titleEl.show();
39296 if(this.config.title){
39297 this.titleTextEl.innerHTML = this.config.title;
39301 if(this.config.collapsed){
39302 this.collapse(true);
39304 if(this.config.hidden){
39308 if (this.unrendered_panels && this.unrendered_panels.length) {
39309 for (var i =0;i< this.unrendered_panels.length; i++) {
39310 this.add(this.unrendered_panels[i]);
39312 this.unrendered_panels = null;
39318 applyConfig : function(c)
39321 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39322 var dh = Roo.DomHelper;
39323 if(c.titlebar !== false){
39324 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39325 this.collapseBtn.on("click", this.collapse, this);
39326 this.collapseBtn.enableDisplayMode();
39328 if(c.showPin === true || this.showPin){
39329 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39330 this.stickBtn.enableDisplayMode();
39331 this.stickBtn.on("click", this.expand, this);
39332 this.stickBtn.hide();
39337 /** This region's collapsed element
39338 * @type Roo.Element */
39341 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39342 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39345 if(c.floatable !== false){
39346 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39347 this.collapsedEl.on("click", this.collapseClick, this);
39350 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39351 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39352 id: "message", unselectable: "on", style:{"float":"left"}});
39353 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39355 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39356 this.expandBtn.on("click", this.expand, this);
39360 if(this.collapseBtn){
39361 this.collapseBtn.setVisible(c.collapsible == true);
39364 this.cmargins = c.cmargins || this.cmargins ||
39365 (this.position == "west" || this.position == "east" ?
39366 {top: 0, left: 2, right:2, bottom: 0} :
39367 {top: 2, left: 0, right:0, bottom: 2});
39369 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39372 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39374 this.autoScroll = c.autoScroll || false;
39379 this.duration = c.duration || .30;
39380 this.slideDuration = c.slideDuration || .45;
39385 * Returns true if this region is currently visible.
39386 * @return {Boolean}
39388 isVisible : function(){
39389 return this.visible;
39393 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39394 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39396 //setCollapsedTitle : function(title){
39397 // title = title || " ";
39398 // if(this.collapsedTitleTextEl){
39399 // this.collapsedTitleTextEl.innerHTML = title;
39403 getBox : function(){
39405 // if(!this.collapsed){
39406 b = this.el.getBox(false, true);
39408 // b = this.collapsedEl.getBox(false, true);
39413 getMargins : function(){
39414 return this.margins;
39415 //return this.collapsed ? this.cmargins : this.margins;
39418 highlight : function(){
39419 this.el.addClass("x-layout-panel-dragover");
39422 unhighlight : function(){
39423 this.el.removeClass("x-layout-panel-dragover");
39426 updateBox : function(box)
39428 if (!this.bodyEl) {
39429 return; // not rendered yet..
39433 if(!this.collapsed){
39434 this.el.dom.style.left = box.x + "px";
39435 this.el.dom.style.top = box.y + "px";
39436 this.updateBody(box.width, box.height);
39438 this.collapsedEl.dom.style.left = box.x + "px";
39439 this.collapsedEl.dom.style.top = box.y + "px";
39440 this.collapsedEl.setSize(box.width, box.height);
39443 this.tabs.autoSizeTabs();
39447 updateBody : function(w, h)
39450 this.el.setWidth(w);
39451 w -= this.el.getBorderWidth("rl");
39452 if(this.config.adjustments){
39453 w += this.config.adjustments[0];
39456 if(h !== null && h > 0){
39457 this.el.setHeight(h);
39458 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39459 h -= this.el.getBorderWidth("tb");
39460 if(this.config.adjustments){
39461 h += this.config.adjustments[1];
39463 this.bodyEl.setHeight(h);
39465 h = this.tabs.syncHeight(h);
39468 if(this.panelSize){
39469 w = w !== null ? w : this.panelSize.width;
39470 h = h !== null ? h : this.panelSize.height;
39472 if(this.activePanel){
39473 var el = this.activePanel.getEl();
39474 w = w !== null ? w : el.getWidth();
39475 h = h !== null ? h : el.getHeight();
39476 this.panelSize = {width: w, height: h};
39477 this.activePanel.setSize(w, h);
39479 if(Roo.isIE && this.tabs){
39480 this.tabs.el.repaint();
39485 * Returns the container element for this region.
39486 * @return {Roo.Element}
39488 getEl : function(){
39493 * Hides this region.
39496 //if(!this.collapsed){
39497 this.el.dom.style.left = "-2000px";
39500 // this.collapsedEl.dom.style.left = "-2000px";
39501 // this.collapsedEl.hide();
39503 this.visible = false;
39504 this.fireEvent("visibilitychange", this, false);
39508 * Shows this region if it was previously hidden.
39511 //if(!this.collapsed){
39514 // this.collapsedEl.show();
39516 this.visible = true;
39517 this.fireEvent("visibilitychange", this, true);
39520 closeClicked : function(){
39521 if(this.activePanel){
39522 this.remove(this.activePanel);
39526 collapseClick : function(e){
39528 e.stopPropagation();
39531 e.stopPropagation();
39537 * Collapses this region.
39538 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39541 collapse : function(skipAnim, skipCheck = false){
39542 if(this.collapsed) {
39546 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39548 this.collapsed = true;
39550 this.split.el.hide();
39552 if(this.config.animate && skipAnim !== true){
39553 this.fireEvent("invalidated", this);
39554 this.animateCollapse();
39556 this.el.setLocation(-20000,-20000);
39558 this.collapsedEl.show();
39559 this.fireEvent("collapsed", this);
39560 this.fireEvent("invalidated", this);
39566 animateCollapse : function(){
39571 * Expands this region if it was previously collapsed.
39572 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39573 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39576 expand : function(e, skipAnim){
39578 e.stopPropagation();
39580 if(!this.collapsed || this.el.hasActiveFx()) {
39584 this.afterSlideIn();
39587 this.collapsed = false;
39588 if(this.config.animate && skipAnim !== true){
39589 this.animateExpand();
39593 this.split.el.show();
39595 this.collapsedEl.setLocation(-2000,-2000);
39596 this.collapsedEl.hide();
39597 this.fireEvent("invalidated", this);
39598 this.fireEvent("expanded", this);
39602 animateExpand : function(){
39606 initTabs : function()
39608 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39610 var ts = new Roo.bootstrap.panel.Tabs({
39611 el: this.bodyEl.dom,
39613 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39614 disableTooltips: this.config.disableTabTips,
39615 toolbar : this.config.toolbar
39618 if(this.config.hideTabs){
39619 ts.stripWrap.setDisplayed(false);
39622 ts.resizeTabs = this.config.resizeTabs === true;
39623 ts.minTabWidth = this.config.minTabWidth || 40;
39624 ts.maxTabWidth = this.config.maxTabWidth || 250;
39625 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39626 ts.monitorResize = false;
39627 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39628 ts.bodyEl.addClass('roo-layout-tabs-body');
39629 this.panels.each(this.initPanelAsTab, this);
39632 initPanelAsTab : function(panel){
39633 var ti = this.tabs.addTab(
39637 this.config.closeOnTab && panel.isClosable(),
39640 if(panel.tabTip !== undefined){
39641 ti.setTooltip(panel.tabTip);
39643 ti.on("activate", function(){
39644 this.setActivePanel(panel);
39647 if(this.config.closeOnTab){
39648 ti.on("beforeclose", function(t, e){
39650 this.remove(panel);
39654 panel.tabItem = ti;
39659 updatePanelTitle : function(panel, title)
39661 if(this.activePanel == panel){
39662 this.updateTitle(title);
39665 var ti = this.tabs.getTab(panel.getEl().id);
39667 if(panel.tabTip !== undefined){
39668 ti.setTooltip(panel.tabTip);
39673 updateTitle : function(title){
39674 if(this.titleTextEl && !this.config.title){
39675 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39679 setActivePanel : function(panel)
39681 panel = this.getPanel(panel);
39682 if(this.activePanel && this.activePanel != panel){
39683 if(this.activePanel.setActiveState(false) === false){
39687 this.activePanel = panel;
39688 panel.setActiveState(true);
39689 if(this.panelSize){
39690 panel.setSize(this.panelSize.width, this.panelSize.height);
39693 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39695 this.updateTitle(panel.getTitle());
39697 this.fireEvent("invalidated", this);
39699 this.fireEvent("panelactivated", this, panel);
39703 * Shows the specified panel.
39704 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39705 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39707 showPanel : function(panel)
39709 panel = this.getPanel(panel);
39712 var tab = this.tabs.getTab(panel.getEl().id);
39713 if(tab.isHidden()){
39714 this.tabs.unhideTab(tab.id);
39718 this.setActivePanel(panel);
39725 * Get the active panel for this region.
39726 * @return {Roo.ContentPanel} The active panel or null
39728 getActivePanel : function(){
39729 return this.activePanel;
39732 validateVisibility : function(){
39733 if(this.panels.getCount() < 1){
39734 this.updateTitle(" ");
39735 this.closeBtn.hide();
39738 if(!this.isVisible()){
39745 * Adds the passed ContentPanel(s) to this region.
39746 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39747 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39749 add : function(panel)
39751 if(arguments.length > 1){
39752 for(var i = 0, len = arguments.length; i < len; i++) {
39753 this.add(arguments[i]);
39758 // if we have not been rendered yet, then we can not really do much of this..
39759 if (!this.bodyEl) {
39760 this.unrendered_panels.push(panel);
39767 if(this.hasPanel(panel)){
39768 this.showPanel(panel);
39771 panel.setRegion(this);
39772 this.panels.add(panel);
39773 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39774 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39775 // and hide them... ???
39776 this.bodyEl.dom.appendChild(panel.getEl().dom);
39777 if(panel.background !== true){
39778 this.setActivePanel(panel);
39780 this.fireEvent("paneladded", this, panel);
39787 this.initPanelAsTab(panel);
39791 if(panel.background !== true){
39792 this.tabs.activate(panel.getEl().id);
39794 this.fireEvent("paneladded", this, panel);
39799 * Hides the tab for the specified panel.
39800 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39802 hidePanel : function(panel){
39803 if(this.tabs && (panel = this.getPanel(panel))){
39804 this.tabs.hideTab(panel.getEl().id);
39809 * Unhides the tab for a previously hidden panel.
39810 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39812 unhidePanel : function(panel){
39813 if(this.tabs && (panel = this.getPanel(panel))){
39814 this.tabs.unhideTab(panel.getEl().id);
39818 clearPanels : function(){
39819 while(this.panels.getCount() > 0){
39820 this.remove(this.panels.first());
39825 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39826 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39827 * @param {Boolean} preservePanel Overrides the config preservePanel option
39828 * @return {Roo.ContentPanel} The panel that was removed
39830 remove : function(panel, preservePanel)
39832 panel = this.getPanel(panel);
39837 this.fireEvent("beforeremove", this, panel, e);
39838 if(e.cancel === true){
39841 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39842 var panelId = panel.getId();
39843 this.panels.removeKey(panelId);
39845 document.body.appendChild(panel.getEl().dom);
39848 this.tabs.removeTab(panel.getEl().id);
39849 }else if (!preservePanel){
39850 this.bodyEl.dom.removeChild(panel.getEl().dom);
39852 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39853 var p = this.panels.first();
39854 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39855 tempEl.appendChild(p.getEl().dom);
39856 this.bodyEl.update("");
39857 this.bodyEl.dom.appendChild(p.getEl().dom);
39859 this.updateTitle(p.getTitle());
39861 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39862 this.setActivePanel(p);
39864 panel.setRegion(null);
39865 if(this.activePanel == panel){
39866 this.activePanel = null;
39868 if(this.config.autoDestroy !== false && preservePanel !== true){
39869 try{panel.destroy();}catch(e){}
39871 this.fireEvent("panelremoved", this, panel);
39876 * Returns the TabPanel component used by this region
39877 * @return {Roo.TabPanel}
39879 getTabs : function(){
39883 createTool : function(parentEl, className){
39884 var btn = Roo.DomHelper.append(parentEl, {
39886 cls: "x-layout-tools-button",
39889 cls: "roo-layout-tools-button-inner " + className,
39893 btn.addClassOnOver("roo-layout-tools-button-over");
39898 * Ext JS Library 1.1.1
39899 * Copyright(c) 2006-2007, Ext JS, LLC.
39901 * Originally Released Under LGPL - original licence link has changed is not relivant.
39904 * <script type="text/javascript">
39910 * @class Roo.SplitLayoutRegion
39911 * @extends Roo.LayoutRegion
39912 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39914 Roo.bootstrap.layout.Split = function(config){
39915 this.cursor = config.cursor;
39916 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39919 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39921 splitTip : "Drag to resize.",
39922 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39923 useSplitTips : false,
39925 applyConfig : function(config){
39926 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39929 onRender : function(ctr,pos) {
39931 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39932 if(!this.config.split){
39937 var splitEl = Roo.DomHelper.append(ctr.dom, {
39939 id: this.el.id + "-split",
39940 cls: "roo-layout-split roo-layout-split-"+this.position,
39943 /** The SplitBar for this region
39944 * @type Roo.SplitBar */
39945 // does not exist yet...
39946 Roo.log([this.position, this.orientation]);
39948 this.split = new Roo.bootstrap.SplitBar({
39949 dragElement : splitEl,
39950 resizingElement: this.el,
39951 orientation : this.orientation
39954 this.split.on("moved", this.onSplitMove, this);
39955 this.split.useShim = this.config.useShim === true;
39956 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39957 if(this.useSplitTips){
39958 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39960 //if(config.collapsible){
39961 // this.split.el.on("dblclick", this.collapse, this);
39964 if(typeof this.config.minSize != "undefined"){
39965 this.split.minSize = this.config.minSize;
39967 if(typeof this.config.maxSize != "undefined"){
39968 this.split.maxSize = this.config.maxSize;
39970 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39971 this.hideSplitter();
39976 getHMaxSize : function(){
39977 var cmax = this.config.maxSize || 10000;
39978 var center = this.mgr.getRegion("center");
39979 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39982 getVMaxSize : function(){
39983 var cmax = this.config.maxSize || 10000;
39984 var center = this.mgr.getRegion("center");
39985 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39988 onSplitMove : function(split, newSize){
39989 this.fireEvent("resized", this, newSize);
39993 * Returns the {@link Roo.SplitBar} for this region.
39994 * @return {Roo.SplitBar}
39996 getSplitBar : function(){
40001 this.hideSplitter();
40002 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40005 hideSplitter : function(){
40007 this.split.el.setLocation(-2000,-2000);
40008 this.split.el.hide();
40014 this.split.el.show();
40016 Roo.bootstrap.layout.Split.superclass.show.call(this);
40019 beforeSlide: function(){
40020 if(Roo.isGecko){// firefox overflow auto bug workaround
40021 this.bodyEl.clip();
40023 this.tabs.bodyEl.clip();
40025 if(this.activePanel){
40026 this.activePanel.getEl().clip();
40028 if(this.activePanel.beforeSlide){
40029 this.activePanel.beforeSlide();
40035 afterSlide : function(){
40036 if(Roo.isGecko){// firefox overflow auto bug workaround
40037 this.bodyEl.unclip();
40039 this.tabs.bodyEl.unclip();
40041 if(this.activePanel){
40042 this.activePanel.getEl().unclip();
40043 if(this.activePanel.afterSlide){
40044 this.activePanel.afterSlide();
40050 initAutoHide : function(){
40051 if(this.autoHide !== false){
40052 if(!this.autoHideHd){
40053 var st = new Roo.util.DelayedTask(this.slideIn, this);
40054 this.autoHideHd = {
40055 "mouseout": function(e){
40056 if(!e.within(this.el, true)){
40060 "mouseover" : function(e){
40066 this.el.on(this.autoHideHd);
40070 clearAutoHide : function(){
40071 if(this.autoHide !== false){
40072 this.el.un("mouseout", this.autoHideHd.mouseout);
40073 this.el.un("mouseover", this.autoHideHd.mouseover);
40077 clearMonitor : function(){
40078 Roo.get(document).un("click", this.slideInIf, this);
40081 // these names are backwards but not changed for compat
40082 slideOut : function(){
40083 if(this.isSlid || this.el.hasActiveFx()){
40086 this.isSlid = true;
40087 if(this.collapseBtn){
40088 this.collapseBtn.hide();
40090 this.closeBtnState = this.closeBtn.getStyle('display');
40091 this.closeBtn.hide();
40093 this.stickBtn.show();
40096 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40097 this.beforeSlide();
40098 this.el.setStyle("z-index", 10001);
40099 this.el.slideIn(this.getSlideAnchor(), {
40100 callback: function(){
40102 this.initAutoHide();
40103 Roo.get(document).on("click", this.slideInIf, this);
40104 this.fireEvent("slideshow", this);
40111 afterSlideIn : function(){
40112 this.clearAutoHide();
40113 this.isSlid = false;
40114 this.clearMonitor();
40115 this.el.setStyle("z-index", "");
40116 if(this.collapseBtn){
40117 this.collapseBtn.show();
40119 this.closeBtn.setStyle('display', this.closeBtnState);
40121 this.stickBtn.hide();
40123 this.fireEvent("slidehide", this);
40126 slideIn : function(cb){
40127 if(!this.isSlid || this.el.hasActiveFx()){
40131 this.isSlid = false;
40132 this.beforeSlide();
40133 this.el.slideOut(this.getSlideAnchor(), {
40134 callback: function(){
40135 this.el.setLeftTop(-10000, -10000);
40137 this.afterSlideIn();
40145 slideInIf : function(e){
40146 if(!e.within(this.el)){
40151 animateCollapse : function(){
40152 this.beforeSlide();
40153 this.el.setStyle("z-index", 20000);
40154 var anchor = this.getSlideAnchor();
40155 this.el.slideOut(anchor, {
40156 callback : function(){
40157 this.el.setStyle("z-index", "");
40158 this.collapsedEl.slideIn(anchor, {duration:.3});
40160 this.el.setLocation(-10000,-10000);
40162 this.fireEvent("collapsed", this);
40169 animateExpand : function(){
40170 this.beforeSlide();
40171 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40172 this.el.setStyle("z-index", 20000);
40173 this.collapsedEl.hide({
40176 this.el.slideIn(this.getSlideAnchor(), {
40177 callback : function(){
40178 this.el.setStyle("z-index", "");
40181 this.split.el.show();
40183 this.fireEvent("invalidated", this);
40184 this.fireEvent("expanded", this);
40212 getAnchor : function(){
40213 return this.anchors[this.position];
40216 getCollapseAnchor : function(){
40217 return this.canchors[this.position];
40220 getSlideAnchor : function(){
40221 return this.sanchors[this.position];
40224 getAlignAdj : function(){
40225 var cm = this.cmargins;
40226 switch(this.position){
40242 getExpandAdj : function(){
40243 var c = this.collapsedEl, cm = this.cmargins;
40244 switch(this.position){
40246 return [-(cm.right+c.getWidth()+cm.left), 0];
40249 return [cm.right+c.getWidth()+cm.left, 0];
40252 return [0, -(cm.top+cm.bottom+c.getHeight())];
40255 return [0, cm.top+cm.bottom+c.getHeight()];
40261 * Ext JS Library 1.1.1
40262 * Copyright(c) 2006-2007, Ext JS, LLC.
40264 * Originally Released Under LGPL - original licence link has changed is not relivant.
40267 * <script type="text/javascript">
40270 * These classes are private internal classes
40272 Roo.bootstrap.layout.Center = function(config){
40273 config.region = "center";
40274 Roo.bootstrap.layout.Region.call(this, config);
40275 this.visible = true;
40276 this.minWidth = config.minWidth || 20;
40277 this.minHeight = config.minHeight || 20;
40280 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40282 // center panel can't be hidden
40286 // center panel can't be hidden
40289 getMinWidth: function(){
40290 return this.minWidth;
40293 getMinHeight: function(){
40294 return this.minHeight;
40308 Roo.bootstrap.layout.North = function(config)
40310 config.region = 'north';
40311 config.cursor = 'n-resize';
40313 Roo.bootstrap.layout.Split.call(this, config);
40317 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40318 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40319 this.split.el.addClass("roo-layout-split-v");
40321 //var size = config.initialSize || config.height;
40322 //if(this.el && typeof size != "undefined"){
40323 // this.el.setHeight(size);
40326 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40328 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40331 onRender : function(ctr, pos)
40333 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40334 var size = this.config.initialSize || this.config.height;
40335 if(this.el && typeof size != "undefined"){
40336 this.el.setHeight(size);
40341 getBox : function(){
40342 if(this.collapsed){
40343 return this.collapsedEl.getBox();
40345 var box = this.el.getBox();
40347 box.height += this.split.el.getHeight();
40352 updateBox : function(box){
40353 if(this.split && !this.collapsed){
40354 box.height -= this.split.el.getHeight();
40355 this.split.el.setLeft(box.x);
40356 this.split.el.setTop(box.y+box.height);
40357 this.split.el.setWidth(box.width);
40359 if(this.collapsed){
40360 this.updateBody(box.width, null);
40362 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40370 Roo.bootstrap.layout.South = function(config){
40371 config.region = 'south';
40372 config.cursor = 's-resize';
40373 Roo.bootstrap.layout.Split.call(this, config);
40375 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40376 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40377 this.split.el.addClass("roo-layout-split-v");
40382 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40383 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40385 onRender : function(ctr, pos)
40387 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40388 var size = this.config.initialSize || this.config.height;
40389 if(this.el && typeof size != "undefined"){
40390 this.el.setHeight(size);
40395 getBox : function(){
40396 if(this.collapsed){
40397 return this.collapsedEl.getBox();
40399 var box = this.el.getBox();
40401 var sh = this.split.el.getHeight();
40408 updateBox : function(box){
40409 if(this.split && !this.collapsed){
40410 var sh = this.split.el.getHeight();
40413 this.split.el.setLeft(box.x);
40414 this.split.el.setTop(box.y-sh);
40415 this.split.el.setWidth(box.width);
40417 if(this.collapsed){
40418 this.updateBody(box.width, null);
40420 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40424 Roo.bootstrap.layout.East = function(config){
40425 config.region = "east";
40426 config.cursor = "e-resize";
40427 Roo.bootstrap.layout.Split.call(this, config);
40429 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40430 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40431 this.split.el.addClass("roo-layout-split-h");
40435 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40436 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40438 onRender : function(ctr, pos)
40440 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40441 var size = this.config.initialSize || this.config.width;
40442 if(this.el && typeof size != "undefined"){
40443 this.el.setWidth(size);
40448 getBox : function(){
40449 if(this.collapsed){
40450 return this.collapsedEl.getBox();
40452 var box = this.el.getBox();
40454 var sw = this.split.el.getWidth();
40461 updateBox : function(box){
40462 if(this.split && !this.collapsed){
40463 var sw = this.split.el.getWidth();
40465 this.split.el.setLeft(box.x);
40466 this.split.el.setTop(box.y);
40467 this.split.el.setHeight(box.height);
40470 if(this.collapsed){
40471 this.updateBody(null, box.height);
40473 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40477 Roo.bootstrap.layout.West = function(config){
40478 config.region = "west";
40479 config.cursor = "w-resize";
40481 Roo.bootstrap.layout.Split.call(this, config);
40483 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40484 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40485 this.split.el.addClass("roo-layout-split-h");
40489 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40490 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40492 onRender: function(ctr, pos)
40494 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40495 var size = this.config.initialSize || this.config.width;
40496 if(typeof size != "undefined"){
40497 this.el.setWidth(size);
40501 getBox : function(){
40502 if(this.collapsed){
40503 return this.collapsedEl.getBox();
40505 var box = this.el.getBox();
40506 if (box.width == 0) {
40507 box.width = this.config.width; // kludge?
40510 box.width += this.split.el.getWidth();
40515 updateBox : function(box){
40516 if(this.split && !this.collapsed){
40517 var sw = this.split.el.getWidth();
40519 this.split.el.setLeft(box.x+box.width);
40520 this.split.el.setTop(box.y);
40521 this.split.el.setHeight(box.height);
40523 if(this.collapsed){
40524 this.updateBody(null, box.height);
40526 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40528 });Roo.namespace("Roo.bootstrap.panel");/*
40530 * Ext JS Library 1.1.1
40531 * Copyright(c) 2006-2007, Ext JS, LLC.
40533 * Originally Released Under LGPL - original licence link has changed is not relivant.
40536 * <script type="text/javascript">
40539 * @class Roo.ContentPanel
40540 * @extends Roo.util.Observable
40541 * A basic ContentPanel element.
40542 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40543 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40544 * @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
40545 * @cfg {Boolean} closable True if the panel can be closed/removed
40546 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40547 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40548 * @cfg {Toolbar} toolbar A toolbar for this panel
40549 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40550 * @cfg {String} title The title for this panel
40551 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40552 * @cfg {String} url Calls {@link #setUrl} with this value
40553 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40554 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40555 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40556 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40557 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40558 * @cfg {Boolean} badges render the badges
40559 * @cfg {String} cls extra classes to use
40560 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40563 * Create a new ContentPanel.
40564 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40565 * @param {String/Object} config A string to set only the title or a config object
40566 * @param {String} content (optional) Set the HTML content for this panel
40567 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40569 Roo.bootstrap.panel.Content = function( config){
40571 this.tpl = config.tpl || false;
40573 var el = config.el;
40574 var content = config.content;
40576 if(config.autoCreate){ // xtype is available if this is called from factory
40579 this.el = Roo.get(el);
40580 if(!this.el && config && config.autoCreate){
40581 if(typeof config.autoCreate == "object"){
40582 if(!config.autoCreate.id){
40583 config.autoCreate.id = config.id||el;
40585 this.el = Roo.DomHelper.append(document.body,
40586 config.autoCreate, true);
40590 cls: (config.cls || '') +
40591 (config.background ? ' bg-' + config.background : '') +
40592 " roo-layout-inactive-content",
40595 if (config.iframe) {
40599 style : 'border: 0px',
40600 src : 'about:blank'
40606 elcfg.html = config.html;
40610 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40611 if (config.iframe) {
40612 this.iframeEl = this.el.select('iframe',true).first();
40617 this.closable = false;
40618 this.loaded = false;
40619 this.active = false;
40622 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40624 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40626 this.wrapEl = this.el; //this.el.wrap();
40628 if (config.toolbar.items) {
40629 ti = config.toolbar.items ;
40630 delete config.toolbar.items ;
40634 this.toolbar.render(this.wrapEl, 'before');
40635 for(var i =0;i < ti.length;i++) {
40636 // Roo.log(['add child', items[i]]);
40637 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40639 this.toolbar.items = nitems;
40640 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40641 delete config.toolbar;
40645 // xtype created footer. - not sure if will work as we normally have to render first..
40646 if (this.footer && !this.footer.el && this.footer.xtype) {
40647 if (!this.wrapEl) {
40648 this.wrapEl = this.el.wrap();
40651 this.footer.container = this.wrapEl.createChild();
40653 this.footer = Roo.factory(this.footer, Roo);
40658 if(typeof config == "string"){
40659 this.title = config;
40661 Roo.apply(this, config);
40665 this.resizeEl = Roo.get(this.resizeEl, true);
40667 this.resizeEl = this.el;
40669 // handle view.xtype
40677 * Fires when this panel is activated.
40678 * @param {Roo.ContentPanel} this
40682 * @event deactivate
40683 * Fires when this panel is activated.
40684 * @param {Roo.ContentPanel} this
40686 "deactivate" : true,
40690 * Fires when this panel is resized if fitToFrame is true.
40691 * @param {Roo.ContentPanel} this
40692 * @param {Number} width The width after any component adjustments
40693 * @param {Number} height The height after any component adjustments
40699 * Fires when this tab is created
40700 * @param {Roo.ContentPanel} this
40706 * Fires when this content is scrolled
40707 * @param {Roo.ContentPanel} this
40708 * @param {Event} scrollEvent
40719 if(this.autoScroll && !this.iframe){
40720 this.resizeEl.setStyle("overflow", "auto");
40721 this.resizeEl.on('scroll', this.onScroll, this);
40723 // fix randome scrolling
40724 //this.el.on('scroll', function() {
40725 // Roo.log('fix random scolling');
40726 // this.scrollTo('top',0);
40729 content = content || this.content;
40731 this.setContent(content);
40733 if(config && config.url){
40734 this.setUrl(this.url, this.params, this.loadOnce);
40739 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40741 if (this.view && typeof(this.view.xtype) != 'undefined') {
40742 this.view.el = this.el.appendChild(document.createElement("div"));
40743 this.view = Roo.factory(this.view);
40744 this.view.render && this.view.render(false, '');
40748 this.fireEvent('render', this);
40751 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40761 /* Resize Element - use this to work out scroll etc. */
40764 setRegion : function(region){
40765 this.region = region;
40766 this.setActiveClass(region && !this.background);
40770 setActiveClass: function(state)
40773 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40774 this.el.setStyle('position','relative');
40776 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40777 this.el.setStyle('position', 'absolute');
40782 * Returns the toolbar for this Panel if one was configured.
40783 * @return {Roo.Toolbar}
40785 getToolbar : function(){
40786 return this.toolbar;
40789 setActiveState : function(active)
40791 this.active = active;
40792 this.setActiveClass(active);
40794 if(this.fireEvent("deactivate", this) === false){
40799 this.fireEvent("activate", this);
40803 * Updates this panel's element (not for iframe)
40804 * @param {String} content The new content
40805 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40807 setContent : function(content, loadScripts){
40812 this.el.update(content, loadScripts);
40815 ignoreResize : function(w, h){
40816 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40819 this.lastSize = {width: w, height: h};
40824 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40825 * @return {Roo.UpdateManager} The UpdateManager
40827 getUpdateManager : function(){
40831 return this.el.getUpdateManager();
40834 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40835 * Does not work with IFRAME contents
40836 * @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:
40839 url: "your-url.php",
40840 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40841 callback: yourFunction,
40842 scope: yourObject, //(optional scope)
40845 text: "Loading...",
40851 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40852 * 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.
40853 * @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}
40854 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40855 * @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.
40856 * @return {Roo.ContentPanel} this
40864 var um = this.el.getUpdateManager();
40865 um.update.apply(um, arguments);
40871 * 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.
40872 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40873 * @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)
40874 * @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)
40875 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40877 setUrl : function(url, params, loadOnce){
40879 this.iframeEl.dom.src = url;
40883 if(this.refreshDelegate){
40884 this.removeListener("activate", this.refreshDelegate);
40886 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40887 this.on("activate", this.refreshDelegate);
40888 return this.el.getUpdateManager();
40891 _handleRefresh : function(url, params, loadOnce){
40892 if(!loadOnce || !this.loaded){
40893 var updater = this.el.getUpdateManager();
40894 updater.update(url, params, this._setLoaded.createDelegate(this));
40898 _setLoaded : function(){
40899 this.loaded = true;
40903 * Returns this panel's id
40906 getId : function(){
40911 * Returns this panel's element - used by regiosn to add.
40912 * @return {Roo.Element}
40914 getEl : function(){
40915 return this.wrapEl || this.el;
40920 adjustForComponents : function(width, height)
40922 //Roo.log('adjustForComponents ');
40923 if(this.resizeEl != this.el){
40924 width -= this.el.getFrameWidth('lr');
40925 height -= this.el.getFrameWidth('tb');
40928 var te = this.toolbar.getEl();
40929 te.setWidth(width);
40930 height -= te.getHeight();
40933 var te = this.footer.getEl();
40934 te.setWidth(width);
40935 height -= te.getHeight();
40939 if(this.adjustments){
40940 width += this.adjustments[0];
40941 height += this.adjustments[1];
40943 return {"width": width, "height": height};
40946 setSize : function(width, height){
40947 if(this.fitToFrame && !this.ignoreResize(width, height)){
40948 if(this.fitContainer && this.resizeEl != this.el){
40949 this.el.setSize(width, height);
40951 var size = this.adjustForComponents(width, height);
40953 this.iframeEl.setSize(width,height);
40956 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40957 this.fireEvent('resize', this, size.width, size.height);
40964 * Returns this panel's title
40967 getTitle : function(){
40969 if (typeof(this.title) != 'object') {
40974 for (var k in this.title) {
40975 if (!this.title.hasOwnProperty(k)) {
40979 if (k.indexOf('-') >= 0) {
40980 var s = k.split('-');
40981 for (var i = 0; i<s.length; i++) {
40982 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40985 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40992 * Set this panel's title
40993 * @param {String} title
40995 setTitle : function(title){
40996 this.title = title;
40998 this.region.updatePanelTitle(this, title);
41003 * Returns true is this panel was configured to be closable
41004 * @return {Boolean}
41006 isClosable : function(){
41007 return this.closable;
41010 beforeSlide : function(){
41012 this.resizeEl.clip();
41015 afterSlide : function(){
41017 this.resizeEl.unclip();
41021 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41022 * Will fail silently if the {@link #setUrl} method has not been called.
41023 * This does not activate the panel, just updates its content.
41025 refresh : function(){
41026 if(this.refreshDelegate){
41027 this.loaded = false;
41028 this.refreshDelegate();
41033 * Destroys this panel
41035 destroy : function(){
41036 this.el.removeAllListeners();
41037 var tempEl = document.createElement("span");
41038 tempEl.appendChild(this.el.dom);
41039 tempEl.innerHTML = "";
41045 * form - if the content panel contains a form - this is a reference to it.
41046 * @type {Roo.form.Form}
41050 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41051 * This contains a reference to it.
41057 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41067 * @param {Object} cfg Xtype definition of item to add.
41071 getChildContainer: function () {
41072 return this.getEl();
41076 onScroll : function(e)
41078 this.fireEvent('scroll', this, e);
41083 var ret = new Roo.factory(cfg);
41088 if (cfg.xtype.match(/^Form$/)) {
41091 //if (this.footer) {
41092 // el = this.footer.container.insertSibling(false, 'before');
41094 el = this.el.createChild();
41097 this.form = new Roo.form.Form(cfg);
41100 if ( this.form.allItems.length) {
41101 this.form.render(el.dom);
41105 // should only have one of theses..
41106 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41107 // views.. should not be just added - used named prop 'view''
41109 cfg.el = this.el.appendChild(document.createElement("div"));
41112 var ret = new Roo.factory(cfg);
41114 ret.render && ret.render(false, ''); // render blank..
41124 * @class Roo.bootstrap.panel.Grid
41125 * @extends Roo.bootstrap.panel.Content
41127 * Create a new GridPanel.
41128 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41129 * @param {Object} config A the config object
41135 Roo.bootstrap.panel.Grid = function(config)
41139 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41140 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41142 config.el = this.wrapper;
41143 //this.el = this.wrapper;
41145 if (config.container) {
41146 // ctor'ed from a Border/panel.grid
41149 this.wrapper.setStyle("overflow", "hidden");
41150 this.wrapper.addClass('roo-grid-container');
41155 if(config.toolbar){
41156 var tool_el = this.wrapper.createChild();
41157 this.toolbar = Roo.factory(config.toolbar);
41159 if (config.toolbar.items) {
41160 ti = config.toolbar.items ;
41161 delete config.toolbar.items ;
41165 this.toolbar.render(tool_el);
41166 for(var i =0;i < ti.length;i++) {
41167 // Roo.log(['add child', items[i]]);
41168 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41170 this.toolbar.items = nitems;
41172 delete config.toolbar;
41175 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41176 config.grid.scrollBody = true;;
41177 config.grid.monitorWindowResize = false; // turn off autosizing
41178 config.grid.autoHeight = false;
41179 config.grid.autoWidth = false;
41181 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41183 if (config.background) {
41184 // render grid on panel activation (if panel background)
41185 this.on('activate', function(gp) {
41186 if (!gp.grid.rendered) {
41187 gp.grid.render(this.wrapper);
41188 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41193 this.grid.render(this.wrapper);
41194 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41197 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41198 // ??? needed ??? config.el = this.wrapper;
41203 // xtype created footer. - not sure if will work as we normally have to render first..
41204 if (this.footer && !this.footer.el && this.footer.xtype) {
41206 var ctr = this.grid.getView().getFooterPanel(true);
41207 this.footer.dataSource = this.grid.dataSource;
41208 this.footer = Roo.factory(this.footer, Roo);
41209 this.footer.render(ctr);
41219 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41220 getId : function(){
41221 return this.grid.id;
41225 * Returns the grid for this panel
41226 * @return {Roo.bootstrap.Table}
41228 getGrid : function(){
41232 setSize : function(width, height){
41233 if(!this.ignoreResize(width, height)){
41234 var grid = this.grid;
41235 var size = this.adjustForComponents(width, height);
41236 // tfoot is not a footer?
41239 var gridel = grid.getGridEl();
41240 gridel.setSize(size.width, size.height);
41242 var tbd = grid.getGridEl().select('tbody', true).first();
41243 var thd = grid.getGridEl().select('thead',true).first();
41244 var tbf= grid.getGridEl().select('tfoot', true).first();
41247 size.height -= tbf.getHeight();
41250 size.height -= thd.getHeight();
41253 tbd.setSize(size.width, size.height );
41254 // this is for the account management tab -seems to work there.
41255 var thd = grid.getGridEl().select('thead',true).first();
41257 // tbd.setSize(size.width, size.height - thd.getHeight());
41266 beforeSlide : function(){
41267 this.grid.getView().scroller.clip();
41270 afterSlide : function(){
41271 this.grid.getView().scroller.unclip();
41274 destroy : function(){
41275 this.grid.destroy();
41277 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41282 * @class Roo.bootstrap.panel.Nest
41283 * @extends Roo.bootstrap.panel.Content
41285 * Create a new Panel, that can contain a layout.Border.
41288 * @param {Roo.BorderLayout} layout The layout for this panel
41289 * @param {String/Object} config A string to set only the title or a config object
41291 Roo.bootstrap.panel.Nest = function(config)
41293 // construct with only one argument..
41294 /* FIXME - implement nicer consturctors
41295 if (layout.layout) {
41297 layout = config.layout;
41298 delete config.layout;
41300 if (layout.xtype && !layout.getEl) {
41301 // then layout needs constructing..
41302 layout = Roo.factory(layout, Roo);
41306 config.el = config.layout.getEl();
41308 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41310 config.layout.monitorWindowResize = false; // turn off autosizing
41311 this.layout = config.layout;
41312 this.layout.getEl().addClass("roo-layout-nested-layout");
41313 this.layout.parent = this;
41320 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41322 setSize : function(width, height){
41323 if(!this.ignoreResize(width, height)){
41324 var size = this.adjustForComponents(width, height);
41325 var el = this.layout.getEl();
41326 if (size.height < 1) {
41327 el.setWidth(size.width);
41329 el.setSize(size.width, size.height);
41331 var touch = el.dom.offsetWidth;
41332 this.layout.layout();
41333 // ie requires a double layout on the first pass
41334 if(Roo.isIE && !this.initialized){
41335 this.initialized = true;
41336 this.layout.layout();
41341 // activate all subpanels if not currently active..
41343 setActiveState : function(active){
41344 this.active = active;
41345 this.setActiveClass(active);
41348 this.fireEvent("deactivate", this);
41352 this.fireEvent("activate", this);
41353 // not sure if this should happen before or after..
41354 if (!this.layout) {
41355 return; // should not happen..
41358 for (var r in this.layout.regions) {
41359 reg = this.layout.getRegion(r);
41360 if (reg.getActivePanel()) {
41361 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41362 reg.setActivePanel(reg.getActivePanel());
41365 if (!reg.panels.length) {
41368 reg.showPanel(reg.getPanel(0));
41377 * Returns the nested BorderLayout for this panel
41378 * @return {Roo.BorderLayout}
41380 getLayout : function(){
41381 return this.layout;
41385 * Adds a xtype elements to the layout of the nested panel
41389 xtype : 'ContentPanel',
41396 xtype : 'NestedLayoutPanel',
41402 items : [ ... list of content panels or nested layout panels.. ]
41406 * @param {Object} cfg Xtype definition of item to add.
41408 addxtype : function(cfg) {
41409 return this.layout.addxtype(cfg);
41414 * Ext JS Library 1.1.1
41415 * Copyright(c) 2006-2007, Ext JS, LLC.
41417 * Originally Released Under LGPL - original licence link has changed is not relivant.
41420 * <script type="text/javascript">
41423 * @class Roo.TabPanel
41424 * @extends Roo.util.Observable
41425 * A lightweight tab container.
41429 // basic tabs 1, built from existing content
41430 var tabs = new Roo.TabPanel("tabs1");
41431 tabs.addTab("script", "View Script");
41432 tabs.addTab("markup", "View Markup");
41433 tabs.activate("script");
41435 // more advanced tabs, built from javascript
41436 var jtabs = new Roo.TabPanel("jtabs");
41437 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41439 // set up the UpdateManager
41440 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41441 var updater = tab2.getUpdateManager();
41442 updater.setDefaultUrl("ajax1.htm");
41443 tab2.on('activate', updater.refresh, updater, true);
41445 // Use setUrl for Ajax loading
41446 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41447 tab3.setUrl("ajax2.htm", null, true);
41450 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41453 jtabs.activate("jtabs-1");
41456 * Create a new TabPanel.
41457 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41458 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41460 Roo.bootstrap.panel.Tabs = function(config){
41462 * The container element for this TabPanel.
41463 * @type Roo.Element
41465 this.el = Roo.get(config.el);
41468 if(typeof config == "boolean"){
41469 this.tabPosition = config ? "bottom" : "top";
41471 Roo.apply(this, config);
41475 if(this.tabPosition == "bottom"){
41476 // if tabs are at the bottom = create the body first.
41477 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41478 this.el.addClass("roo-tabs-bottom");
41480 // next create the tabs holders
41482 if (this.tabPosition == "west"){
41484 var reg = this.region; // fake it..
41486 if (!reg.mgr.parent) {
41489 reg = reg.mgr.parent.region;
41491 Roo.log("got nest?");
41493 if (reg.mgr.getRegion('west')) {
41494 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41495 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41496 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41497 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41498 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41506 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41507 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41508 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41509 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41514 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41517 // finally - if tabs are at the top, then create the body last..
41518 if(this.tabPosition != "bottom"){
41519 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41520 * @type Roo.Element
41522 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41523 this.el.addClass("roo-tabs-top");
41527 this.bodyEl.setStyle("position", "relative");
41529 this.active = null;
41530 this.activateDelegate = this.activate.createDelegate(this);
41535 * Fires when the active tab changes
41536 * @param {Roo.TabPanel} this
41537 * @param {Roo.TabPanelItem} activePanel The new active tab
41541 * @event beforetabchange
41542 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41543 * @param {Roo.TabPanel} this
41544 * @param {Object} e Set cancel to true on this object to cancel the tab change
41545 * @param {Roo.TabPanelItem} tab The tab being changed to
41547 "beforetabchange" : true
41550 Roo.EventManager.onWindowResize(this.onResize, this);
41551 this.cpad = this.el.getPadding("lr");
41552 this.hiddenCount = 0;
41555 // toolbar on the tabbar support...
41556 if (this.toolbar) {
41557 alert("no toolbar support yet");
41558 this.toolbar = false;
41560 var tcfg = this.toolbar;
41561 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41562 this.toolbar = new Roo.Toolbar(tcfg);
41563 if (Roo.isSafari) {
41564 var tbl = tcfg.container.child('table', true);
41565 tbl.setAttribute('width', '100%');
41573 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41576 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41578 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41580 tabPosition : "top",
41582 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41584 currentTabWidth : 0,
41586 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41590 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41594 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41596 preferredTabWidth : 175,
41598 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41600 resizeTabs : false,
41602 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41604 monitorResize : true,
41606 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41608 toolbar : false, // set by caller..
41610 region : false, /// set by caller
41612 disableTooltips : true, // not used yet...
41615 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41616 * @param {String} id The id of the div to use <b>or create</b>
41617 * @param {String} text The text for the tab
41618 * @param {String} content (optional) Content to put in the TabPanelItem body
41619 * @param {Boolean} closable (optional) True to create a close icon on the tab
41620 * @return {Roo.TabPanelItem} The created TabPanelItem
41622 addTab : function(id, text, content, closable, tpl)
41624 var item = new Roo.bootstrap.panel.TabItem({
41628 closable : closable,
41631 this.addTabItem(item);
41633 item.setContent(content);
41639 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41640 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41641 * @return {Roo.TabPanelItem}
41643 getTab : function(id){
41644 return this.items[id];
41648 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41649 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41651 hideTab : function(id){
41652 var t = this.items[id];
41655 this.hiddenCount++;
41656 this.autoSizeTabs();
41661 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41662 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41664 unhideTab : function(id){
41665 var t = this.items[id];
41667 t.setHidden(false);
41668 this.hiddenCount--;
41669 this.autoSizeTabs();
41674 * Adds an existing {@link Roo.TabPanelItem}.
41675 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41677 addTabItem : function(item)
41679 this.items[item.id] = item;
41680 this.items.push(item);
41681 this.autoSizeTabs();
41682 // if(this.resizeTabs){
41683 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41684 // this.autoSizeTabs();
41686 // item.autoSize();
41691 * Removes a {@link Roo.TabPanelItem}.
41692 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41694 removeTab : function(id){
41695 var items = this.items;
41696 var tab = items[id];
41697 if(!tab) { return; }
41698 var index = items.indexOf(tab);
41699 if(this.active == tab && items.length > 1){
41700 var newTab = this.getNextAvailable(index);
41705 this.stripEl.dom.removeChild(tab.pnode.dom);
41706 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41707 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41709 items.splice(index, 1);
41710 delete this.items[tab.id];
41711 tab.fireEvent("close", tab);
41712 tab.purgeListeners();
41713 this.autoSizeTabs();
41716 getNextAvailable : function(start){
41717 var items = this.items;
41719 // look for a next tab that will slide over to
41720 // replace the one being removed
41721 while(index < items.length){
41722 var item = items[++index];
41723 if(item && !item.isHidden()){
41727 // if one isn't found select the previous tab (on the left)
41730 var item = items[--index];
41731 if(item && !item.isHidden()){
41739 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41740 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41742 disableTab : function(id){
41743 var tab = this.items[id];
41744 if(tab && this.active != tab){
41750 * Enables a {@link Roo.TabPanelItem} that is disabled.
41751 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41753 enableTab : function(id){
41754 var tab = this.items[id];
41759 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41760 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41761 * @return {Roo.TabPanelItem} The TabPanelItem.
41763 activate : function(id)
41765 //Roo.log('activite:' + id);
41767 var tab = this.items[id];
41771 if(tab == this.active || tab.disabled){
41775 this.fireEvent("beforetabchange", this, e, tab);
41776 if(e.cancel !== true && !tab.disabled){
41778 this.active.hide();
41780 this.active = this.items[id];
41781 this.active.show();
41782 this.fireEvent("tabchange", this, this.active);
41788 * Gets the active {@link Roo.TabPanelItem}.
41789 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41791 getActiveTab : function(){
41792 return this.active;
41796 * Updates the tab body element to fit the height of the container element
41797 * for overflow scrolling
41798 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41800 syncHeight : function(targetHeight){
41801 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41802 var bm = this.bodyEl.getMargins();
41803 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41804 this.bodyEl.setHeight(newHeight);
41808 onResize : function(){
41809 if(this.monitorResize){
41810 this.autoSizeTabs();
41815 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41817 beginUpdate : function(){
41818 this.updating = true;
41822 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41824 endUpdate : function(){
41825 this.updating = false;
41826 this.autoSizeTabs();
41830 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41832 autoSizeTabs : function()
41834 var count = this.items.length;
41835 var vcount = count - this.hiddenCount;
41838 this.stripEl.hide();
41840 this.stripEl.show();
41843 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41848 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41849 var availWidth = Math.floor(w / vcount);
41850 var b = this.stripBody;
41851 if(b.getWidth() > w){
41852 var tabs = this.items;
41853 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41854 if(availWidth < this.minTabWidth){
41855 /*if(!this.sleft){ // incomplete scrolling code
41856 this.createScrollButtons();
41859 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41862 if(this.currentTabWidth < this.preferredTabWidth){
41863 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41869 * Returns the number of tabs in this TabPanel.
41872 getCount : function(){
41873 return this.items.length;
41877 * Resizes all the tabs to the passed width
41878 * @param {Number} The new width
41880 setTabWidth : function(width){
41881 this.currentTabWidth = width;
41882 for(var i = 0, len = this.items.length; i < len; i++) {
41883 if(!this.items[i].isHidden()) {
41884 this.items[i].setWidth(width);
41890 * Destroys this TabPanel
41891 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41893 destroy : function(removeEl){
41894 Roo.EventManager.removeResizeListener(this.onResize, this);
41895 for(var i = 0, len = this.items.length; i < len; i++){
41896 this.items[i].purgeListeners();
41898 if(removeEl === true){
41899 this.el.update("");
41904 createStrip : function(container)
41906 var strip = document.createElement("nav");
41907 strip.className = Roo.bootstrap.version == 4 ?
41908 "navbar-light bg-light" :
41909 "navbar navbar-default"; //"x-tabs-wrap";
41910 container.appendChild(strip);
41914 createStripList : function(strip)
41916 // div wrapper for retard IE
41917 // returns the "tr" element.
41918 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41919 //'<div class="x-tabs-strip-wrap">'+
41920 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41921 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41922 return strip.firstChild; //.firstChild.firstChild.firstChild;
41924 createBody : function(container)
41926 var body = document.createElement("div");
41927 Roo.id(body, "tab-body");
41928 //Roo.fly(body).addClass("x-tabs-body");
41929 Roo.fly(body).addClass("tab-content");
41930 container.appendChild(body);
41933 createItemBody :function(bodyEl, id){
41934 var body = Roo.getDom(id);
41936 body = document.createElement("div");
41939 //Roo.fly(body).addClass("x-tabs-item-body");
41940 Roo.fly(body).addClass("tab-pane");
41941 bodyEl.insertBefore(body, bodyEl.firstChild);
41945 createStripElements : function(stripEl, text, closable, tpl)
41947 var td = document.createElement("li"); // was td..
41948 td.className = 'nav-item';
41950 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41953 stripEl.appendChild(td);
41955 td.className = "x-tabs-closable";
41956 if(!this.closeTpl){
41957 this.closeTpl = new Roo.Template(
41958 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41959 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41960 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41963 var el = this.closeTpl.overwrite(td, {"text": text});
41964 var close = el.getElementsByTagName("div")[0];
41965 var inner = el.getElementsByTagName("em")[0];
41966 return {"el": el, "close": close, "inner": inner};
41969 // not sure what this is..
41970 // if(!this.tabTpl){
41971 //this.tabTpl = new Roo.Template(
41972 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41973 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41975 // this.tabTpl = new Roo.Template(
41976 // '<a href="#">' +
41977 // '<span unselectable="on"' +
41978 // (this.disableTooltips ? '' : ' title="{text}"') +
41979 // ' >{text}</span></a>'
41985 var template = tpl || this.tabTpl || false;
41988 template = new Roo.Template(
41989 Roo.bootstrap.version == 4 ?
41991 '<a class="nav-link" href="#" unselectable="on"' +
41992 (this.disableTooltips ? '' : ' title="{text}"') +
41995 '<a class="nav-link" href="#">' +
41996 '<span unselectable="on"' +
41997 (this.disableTooltips ? '' : ' title="{text}"') +
41998 ' >{text}</span></a>'
42003 switch (typeof(template)) {
42007 template = new Roo.Template(template);
42013 var el = template.overwrite(td, {"text": text});
42015 var inner = el.getElementsByTagName("span")[0];
42017 return {"el": el, "inner": inner};
42025 * @class Roo.TabPanelItem
42026 * @extends Roo.util.Observable
42027 * Represents an individual item (tab plus body) in a TabPanel.
42028 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42029 * @param {String} id The id of this TabPanelItem
42030 * @param {String} text The text for the tab of this TabPanelItem
42031 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42033 Roo.bootstrap.panel.TabItem = function(config){
42035 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42036 * @type Roo.TabPanel
42038 this.tabPanel = config.panel;
42040 * The id for this TabPanelItem
42043 this.id = config.id;
42045 this.disabled = false;
42047 this.text = config.text;
42049 this.loaded = false;
42050 this.closable = config.closable;
42053 * The body element for this TabPanelItem.
42054 * @type Roo.Element
42056 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42057 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42058 this.bodyEl.setStyle("display", "block");
42059 this.bodyEl.setStyle("zoom", "1");
42060 //this.hideAction();
42062 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42064 this.el = Roo.get(els.el);
42065 this.inner = Roo.get(els.inner, true);
42066 this.textEl = Roo.bootstrap.version == 4 ?
42067 this.el : Roo.get(this.el.dom.firstChild, true);
42069 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42070 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42073 // this.el.on("mousedown", this.onTabMouseDown, this);
42074 this.el.on("click", this.onTabClick, this);
42076 if(config.closable){
42077 var c = Roo.get(els.close, true);
42078 c.dom.title = this.closeText;
42079 c.addClassOnOver("close-over");
42080 c.on("click", this.closeClick, this);
42086 * Fires when this tab becomes the active tab.
42087 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42088 * @param {Roo.TabPanelItem} this
42092 * @event beforeclose
42093 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42094 * @param {Roo.TabPanelItem} this
42095 * @param {Object} e Set cancel to true on this object to cancel the close.
42097 "beforeclose": true,
42100 * Fires when this tab is closed.
42101 * @param {Roo.TabPanelItem} this
42105 * @event deactivate
42106 * Fires when this tab is no longer the active tab.
42107 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42108 * @param {Roo.TabPanelItem} this
42110 "deactivate" : true
42112 this.hidden = false;
42114 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42117 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42119 purgeListeners : function(){
42120 Roo.util.Observable.prototype.purgeListeners.call(this);
42121 this.el.removeAllListeners();
42124 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42127 this.status_node.addClass("active");
42130 this.tabPanel.stripWrap.repaint();
42132 this.fireEvent("activate", this.tabPanel, this);
42136 * Returns true if this tab is the active tab.
42137 * @return {Boolean}
42139 isActive : function(){
42140 return this.tabPanel.getActiveTab() == this;
42144 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42147 this.status_node.removeClass("active");
42149 this.fireEvent("deactivate", this.tabPanel, this);
42152 hideAction : function(){
42153 this.bodyEl.hide();
42154 this.bodyEl.setStyle("position", "absolute");
42155 this.bodyEl.setLeft("-20000px");
42156 this.bodyEl.setTop("-20000px");
42159 showAction : function(){
42160 this.bodyEl.setStyle("position", "relative");
42161 this.bodyEl.setTop("");
42162 this.bodyEl.setLeft("");
42163 this.bodyEl.show();
42167 * Set the tooltip for the tab.
42168 * @param {String} tooltip The tab's tooltip
42170 setTooltip : function(text){
42171 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42172 this.textEl.dom.qtip = text;
42173 this.textEl.dom.removeAttribute('title');
42175 this.textEl.dom.title = text;
42179 onTabClick : function(e){
42180 e.preventDefault();
42181 this.tabPanel.activate(this.id);
42184 onTabMouseDown : function(e){
42185 e.preventDefault();
42186 this.tabPanel.activate(this.id);
42189 getWidth : function(){
42190 return this.inner.getWidth();
42193 setWidth : function(width){
42194 var iwidth = width - this.linode.getPadding("lr");
42195 this.inner.setWidth(iwidth);
42196 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42197 this.linode.setWidth(width);
42201 * Show or hide the tab
42202 * @param {Boolean} hidden True to hide or false to show.
42204 setHidden : function(hidden){
42205 this.hidden = hidden;
42206 this.linode.setStyle("display", hidden ? "none" : "");
42210 * Returns true if this tab is "hidden"
42211 * @return {Boolean}
42213 isHidden : function(){
42214 return this.hidden;
42218 * Returns the text for this tab
42221 getText : function(){
42225 autoSize : function(){
42226 //this.el.beginMeasure();
42227 this.textEl.setWidth(1);
42229 * #2804 [new] Tabs in Roojs
42230 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42232 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42233 //this.el.endMeasure();
42237 * Sets the text for the tab (Note: this also sets the tooltip text)
42238 * @param {String} text The tab's text and tooltip
42240 setText : function(text){
42242 this.textEl.update(text);
42243 this.setTooltip(text);
42244 //if(!this.tabPanel.resizeTabs){
42245 // this.autoSize();
42249 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42251 activate : function(){
42252 this.tabPanel.activate(this.id);
42256 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42258 disable : function(){
42259 if(this.tabPanel.active != this){
42260 this.disabled = true;
42261 this.status_node.addClass("disabled");
42266 * Enables this TabPanelItem if it was previously disabled.
42268 enable : function(){
42269 this.disabled = false;
42270 this.status_node.removeClass("disabled");
42274 * Sets the content for this TabPanelItem.
42275 * @param {String} content The content
42276 * @param {Boolean} loadScripts true to look for and load scripts
42278 setContent : function(content, loadScripts){
42279 this.bodyEl.update(content, loadScripts);
42283 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42284 * @return {Roo.UpdateManager} The UpdateManager
42286 getUpdateManager : function(){
42287 return this.bodyEl.getUpdateManager();
42291 * Set a URL to be used to load the content for this TabPanelItem.
42292 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42293 * @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)
42294 * @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)
42295 * @return {Roo.UpdateManager} The UpdateManager
42297 setUrl : function(url, params, loadOnce){
42298 if(this.refreshDelegate){
42299 this.un('activate', this.refreshDelegate);
42301 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42302 this.on("activate", this.refreshDelegate);
42303 return this.bodyEl.getUpdateManager();
42307 _handleRefresh : function(url, params, loadOnce){
42308 if(!loadOnce || !this.loaded){
42309 var updater = this.bodyEl.getUpdateManager();
42310 updater.update(url, params, this._setLoaded.createDelegate(this));
42315 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42316 * Will fail silently if the setUrl method has not been called.
42317 * This does not activate the panel, just updates its content.
42319 refresh : function(){
42320 if(this.refreshDelegate){
42321 this.loaded = false;
42322 this.refreshDelegate();
42327 _setLoaded : function(){
42328 this.loaded = true;
42332 closeClick : function(e){
42335 this.fireEvent("beforeclose", this, o);
42336 if(o.cancel !== true){
42337 this.tabPanel.removeTab(this.id);
42341 * The text displayed in the tooltip for the close icon.
42344 closeText : "Close this tab"
42347 * This script refer to:
42348 * Title: International Telephone Input
42349 * Author: Jack O'Connor
42350 * Code version: v12.1.12
42351 * Availability: https://github.com/jackocnr/intl-tel-input.git
42354 Roo.bootstrap.PhoneInputData = function() {
42357 "Afghanistan (افغانستان)",
42362 "Albania (Shqipëri)",
42367 "Algeria (الجزائر)",
42392 "Antigua and Barbuda",
42402 "Armenia (Հայաստան)",
42418 "Austria (Österreich)",
42423 "Azerbaijan (Azərbaycan)",
42433 "Bahrain (البحرين)",
42438 "Bangladesh (বাংলাদেশ)",
42448 "Belarus (Беларусь)",
42453 "Belgium (België)",
42483 "Bosnia and Herzegovina (Босна и Херцеговина)",
42498 "British Indian Ocean Territory",
42503 "British Virgin Islands",
42513 "Bulgaria (България)",
42523 "Burundi (Uburundi)",
42528 "Cambodia (កម្ពុជា)",
42533 "Cameroon (Cameroun)",
42542 ["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"]
42545 "Cape Verde (Kabu Verdi)",
42550 "Caribbean Netherlands",
42561 "Central African Republic (République centrafricaine)",
42581 "Christmas Island",
42587 "Cocos (Keeling) Islands",
42598 "Comoros (جزر القمر)",
42603 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42608 "Congo (Republic) (Congo-Brazzaville)",
42628 "Croatia (Hrvatska)",
42649 "Czech Republic (Česká republika)",
42654 "Denmark (Danmark)",
42669 "Dominican Republic (República Dominicana)",
42673 ["809", "829", "849"]
42691 "Equatorial Guinea (Guinea Ecuatorial)",
42711 "Falkland Islands (Islas Malvinas)",
42716 "Faroe Islands (Føroyar)",
42737 "French Guiana (Guyane française)",
42742 "French Polynesia (Polynésie française)",
42757 "Georgia (საქართველო)",
42762 "Germany (Deutschland)",
42782 "Greenland (Kalaallit Nunaat)",
42819 "Guinea-Bissau (Guiné Bissau)",
42844 "Hungary (Magyarország)",
42849 "Iceland (Ísland)",
42869 "Iraq (العراق)",
42885 "Israel (ישראל)",
42912 "Jordan (الأردن)",
42917 "Kazakhstan (Казахстан)",
42938 "Kuwait (الكويت)",
42943 "Kyrgyzstan (Кыргызстан)",
42953 "Latvia (Latvija)",
42958 "Lebanon (لبنان)",
42973 "Libya (ليبيا)",
42983 "Lithuania (Lietuva)",
42998 "Macedonia (FYROM) (Македонија)",
43003 "Madagascar (Madagasikara)",
43033 "Marshall Islands",
43043 "Mauritania (موريتانيا)",
43048 "Mauritius (Moris)",
43069 "Moldova (Republica Moldova)",
43079 "Mongolia (Монгол)",
43084 "Montenegro (Crna Gora)",
43094 "Morocco (المغرب)",
43100 "Mozambique (Moçambique)",
43105 "Myanmar (Burma) (မြန်မာ)",
43110 "Namibia (Namibië)",
43125 "Netherlands (Nederland)",
43130 "New Caledonia (Nouvelle-Calédonie)",
43165 "North Korea (조선 민주주의 인민 공화국)",
43170 "Northern Mariana Islands",
43186 "Pakistan (پاکستان)",
43196 "Palestine (فلسطين)",
43206 "Papua New Guinea",
43248 "Réunion (La Réunion)",
43254 "Romania (România)",
43270 "Saint Barthélemy",
43281 "Saint Kitts and Nevis",
43291 "Saint Martin (Saint-Martin (partie française))",
43297 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43302 "Saint Vincent and the Grenadines",
43317 "São Tomé and Príncipe (São Tomé e Príncipe)",
43322 "Saudi Arabia (المملكة العربية السعودية)",
43327 "Senegal (Sénégal)",
43357 "Slovakia (Slovensko)",
43362 "Slovenia (Slovenija)",
43372 "Somalia (Soomaaliya)",
43382 "South Korea (대한민국)",
43387 "South Sudan (جنوب السودان)",
43397 "Sri Lanka (ශ්රී ලංකාව)",
43402 "Sudan (السودان)",
43412 "Svalbard and Jan Mayen",
43423 "Sweden (Sverige)",
43428 "Switzerland (Schweiz)",
43433 "Syria (سوريا)",
43478 "Trinidad and Tobago",
43483 "Tunisia (تونس)",
43488 "Turkey (Türkiye)",
43498 "Turks and Caicos Islands",
43508 "U.S. Virgin Islands",
43518 "Ukraine (Україна)",
43523 "United Arab Emirates (الإمارات العربية المتحدة)",
43545 "Uzbekistan (Oʻzbekiston)",
43555 "Vatican City (Città del Vaticano)",
43566 "Vietnam (Việt Nam)",
43571 "Wallis and Futuna (Wallis-et-Futuna)",
43576 "Western Sahara (الصحراء الغربية)",
43582 "Yemen (اليمن)",
43606 * This script refer to:
43607 * Title: International Telephone Input
43608 * Author: Jack O'Connor
43609 * Code version: v12.1.12
43610 * Availability: https://github.com/jackocnr/intl-tel-input.git
43614 * @class Roo.bootstrap.PhoneInput
43615 * @extends Roo.bootstrap.TriggerField
43616 * An input with International dial-code selection
43618 * @cfg {String} defaultDialCode default '+852'
43619 * @cfg {Array} preferedCountries default []
43622 * Create a new PhoneInput.
43623 * @param {Object} config Configuration options
43626 Roo.bootstrap.PhoneInput = function(config) {
43627 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43630 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43632 listWidth: undefined,
43634 selectedClass: 'active',
43636 invalidClass : "has-warning",
43638 validClass: 'has-success',
43640 allowed: '0123456789',
43645 * @cfg {String} defaultDialCode The default dial code when initializing the input
43647 defaultDialCode: '+852',
43650 * @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
43652 preferedCountries: false,
43654 getAutoCreate : function()
43656 var data = Roo.bootstrap.PhoneInputData();
43657 var align = this.labelAlign || this.parentLabelAlign();
43660 this.allCountries = [];
43661 this.dialCodeMapping = [];
43663 for (var i = 0; i < data.length; i++) {
43665 this.allCountries[i] = {
43669 priority: c[3] || 0,
43670 areaCodes: c[4] || null
43672 this.dialCodeMapping[c[2]] = {
43675 priority: c[3] || 0,
43676 areaCodes: c[4] || null
43688 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43689 maxlength: this.max_length,
43690 cls : 'form-control tel-input',
43691 autocomplete: 'new-password'
43694 var hiddenInput = {
43697 cls: 'hidden-tel-input'
43701 hiddenInput.name = this.name;
43704 if (this.disabled) {
43705 input.disabled = true;
43708 var flag_container = {
43725 cls: this.hasFeedback ? 'has-feedback' : '',
43731 cls: 'dial-code-holder',
43738 cls: 'roo-select2-container input-group',
43745 if (this.fieldLabel.length) {
43748 tooltip: 'This field is required'
43754 cls: 'control-label',
43760 html: this.fieldLabel
43763 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43769 if(this.indicatorpos == 'right') {
43770 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43777 if(align == 'left') {
43785 if(this.labelWidth > 12){
43786 label.style = "width: " + this.labelWidth + 'px';
43788 if(this.labelWidth < 13 && this.labelmd == 0){
43789 this.labelmd = this.labelWidth;
43791 if(this.labellg > 0){
43792 label.cls += ' col-lg-' + this.labellg;
43793 input.cls += ' col-lg-' + (12 - this.labellg);
43795 if(this.labelmd > 0){
43796 label.cls += ' col-md-' + this.labelmd;
43797 container.cls += ' col-md-' + (12 - this.labelmd);
43799 if(this.labelsm > 0){
43800 label.cls += ' col-sm-' + this.labelsm;
43801 container.cls += ' col-sm-' + (12 - this.labelsm);
43803 if(this.labelxs > 0){
43804 label.cls += ' col-xs-' + this.labelxs;
43805 container.cls += ' col-xs-' + (12 - this.labelxs);
43815 var settings = this;
43817 ['xs','sm','md','lg'].map(function(size){
43818 if (settings[size]) {
43819 cfg.cls += ' col-' + size + '-' + settings[size];
43823 this.store = new Roo.data.Store({
43824 proxy : new Roo.data.MemoryProxy({}),
43825 reader : new Roo.data.JsonReader({
43836 'name' : 'dialCode',
43840 'name' : 'priority',
43844 'name' : 'areaCodes',
43851 if(!this.preferedCountries) {
43852 this.preferedCountries = [
43859 var p = this.preferedCountries.reverse();
43862 for (var i = 0; i < p.length; i++) {
43863 for (var j = 0; j < this.allCountries.length; j++) {
43864 if(this.allCountries[j].iso2 == p[i]) {
43865 var t = this.allCountries[j];
43866 this.allCountries.splice(j,1);
43867 this.allCountries.unshift(t);
43873 this.store.proxy.data = {
43875 data: this.allCountries
43881 initEvents : function()
43884 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43886 this.indicator = this.indicatorEl();
43887 this.flag = this.flagEl();
43888 this.dialCodeHolder = this.dialCodeHolderEl();
43890 this.trigger = this.el.select('div.flag-box',true).first();
43891 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43896 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43897 _this.list.setWidth(lw);
43900 this.list.on('mouseover', this.onViewOver, this);
43901 this.list.on('mousemove', this.onViewMove, this);
43902 this.inputEl().on("keyup", this.onKeyUp, this);
43903 this.inputEl().on("keypress", this.onKeyPress, this);
43905 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43907 this.view = new Roo.View(this.list, this.tpl, {
43908 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43911 this.view.on('click', this.onViewClick, this);
43912 this.setValue(this.defaultDialCode);
43915 onTriggerClick : function(e)
43917 Roo.log('trigger click');
43922 if(this.isExpanded()){
43924 this.hasFocus = false;
43926 this.store.load({});
43927 this.hasFocus = true;
43932 isExpanded : function()
43934 return this.list.isVisible();
43937 collapse : function()
43939 if(!this.isExpanded()){
43943 Roo.get(document).un('mousedown', this.collapseIf, this);
43944 Roo.get(document).un('mousewheel', this.collapseIf, this);
43945 this.fireEvent('collapse', this);
43949 expand : function()
43953 if(this.isExpanded() || !this.hasFocus){
43957 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43958 this.list.setWidth(lw);
43961 this.restrictHeight();
43963 Roo.get(document).on('mousedown', this.collapseIf, this);
43964 Roo.get(document).on('mousewheel', this.collapseIf, this);
43966 this.fireEvent('expand', this);
43969 restrictHeight : function()
43971 this.list.alignTo(this.inputEl(), this.listAlign);
43972 this.list.alignTo(this.inputEl(), this.listAlign);
43975 onViewOver : function(e, t)
43977 if(this.inKeyMode){
43980 var item = this.view.findItemFromChild(t);
43983 var index = this.view.indexOf(item);
43984 this.select(index, false);
43989 onViewClick : function(view, doFocus, el, e)
43991 var index = this.view.getSelectedIndexes()[0];
43993 var r = this.store.getAt(index);
43996 this.onSelect(r, index);
43998 if(doFocus !== false && !this.blockFocus){
43999 this.inputEl().focus();
44003 onViewMove : function(e, t)
44005 this.inKeyMode = false;
44008 select : function(index, scrollIntoView)
44010 this.selectedIndex = index;
44011 this.view.select(index);
44012 if(scrollIntoView !== false){
44013 var el = this.view.getNode(index);
44015 this.list.scrollChildIntoView(el, false);
44020 createList : function()
44022 this.list = Roo.get(document.body).createChild({
44024 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44025 style: 'display:none'
44028 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44031 collapseIf : function(e)
44033 var in_combo = e.within(this.el);
44034 var in_list = e.within(this.list);
44035 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44037 if (in_combo || in_list || is_list) {
44043 onSelect : function(record, index)
44045 if(this.fireEvent('beforeselect', this, record, index) !== false){
44047 this.setFlagClass(record.data.iso2);
44048 this.setDialCode(record.data.dialCode);
44049 this.hasFocus = false;
44051 this.fireEvent('select', this, record, index);
44055 flagEl : function()
44057 var flag = this.el.select('div.flag',true).first();
44064 dialCodeHolderEl : function()
44066 var d = this.el.select('input.dial-code-holder',true).first();
44073 setDialCode : function(v)
44075 this.dialCodeHolder.dom.value = '+'+v;
44078 setFlagClass : function(n)
44080 this.flag.dom.className = 'flag '+n;
44083 getValue : function()
44085 var v = this.inputEl().getValue();
44086 if(this.dialCodeHolder) {
44087 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44092 setValue : function(v)
44094 var d = this.getDialCode(v);
44096 //invalid dial code
44097 if(v.length == 0 || !d || d.length == 0) {
44099 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44100 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44106 this.setFlagClass(this.dialCodeMapping[d].iso2);
44107 this.setDialCode(d);
44108 this.inputEl().dom.value = v.replace('+'+d,'');
44109 this.hiddenEl().dom.value = this.getValue();
44114 getDialCode : function(v)
44118 if (v.length == 0) {
44119 return this.dialCodeHolder.dom.value;
44123 if (v.charAt(0) != "+") {
44126 var numericChars = "";
44127 for (var i = 1; i < v.length; i++) {
44128 var c = v.charAt(i);
44131 if (this.dialCodeMapping[numericChars]) {
44132 dialCode = v.substr(1, i);
44134 if (numericChars.length == 4) {
44144 this.setValue(this.defaultDialCode);
44148 hiddenEl : function()
44150 return this.el.select('input.hidden-tel-input',true).first();
44153 // after setting val
44154 onKeyUp : function(e){
44155 this.setValue(this.getValue());
44158 onKeyPress : function(e){
44159 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44166 * @class Roo.bootstrap.MoneyField
44167 * @extends Roo.bootstrap.ComboBox
44168 * Bootstrap MoneyField class
44171 * Create a new MoneyField.
44172 * @param {Object} config Configuration options
44175 Roo.bootstrap.MoneyField = function(config) {
44177 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44181 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44184 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44186 allowDecimals : true,
44188 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44190 decimalSeparator : ".",
44192 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44194 decimalPrecision : 0,
44196 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44198 allowNegative : true,
44200 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44204 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44206 minValue : Number.NEGATIVE_INFINITY,
44208 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44210 maxValue : Number.MAX_VALUE,
44212 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44214 minText : "The minimum value for this field is {0}",
44216 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44218 maxText : "The maximum value for this field is {0}",
44220 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44221 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44223 nanText : "{0} is not a valid number",
44225 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44229 * @cfg {String} defaults currency of the MoneyField
44230 * value should be in lkey
44232 defaultCurrency : false,
44234 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44236 thousandsDelimiter : false,
44238 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44249 getAutoCreate : function()
44251 var align = this.labelAlign || this.parentLabelAlign();
44263 cls : 'form-control roo-money-amount-input',
44264 autocomplete: 'new-password'
44267 var hiddenInput = {
44271 cls: 'hidden-number-input'
44274 if(this.max_length) {
44275 input.maxlength = this.max_length;
44279 hiddenInput.name = this.name;
44282 if (this.disabled) {
44283 input.disabled = true;
44286 var clg = 12 - this.inputlg;
44287 var cmd = 12 - this.inputmd;
44288 var csm = 12 - this.inputsm;
44289 var cxs = 12 - this.inputxs;
44293 cls : 'row roo-money-field',
44297 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44301 cls: 'roo-select2-container input-group',
44305 cls : 'form-control roo-money-currency-input',
44306 autocomplete: 'new-password',
44308 name : this.currencyName
44312 cls : 'input-group-addon',
44326 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44330 cls: this.hasFeedback ? 'has-feedback' : '',
44341 if (this.fieldLabel.length) {
44344 tooltip: 'This field is required'
44350 cls: 'control-label',
44356 html: this.fieldLabel
44359 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44365 if(this.indicatorpos == 'right') {
44366 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44373 if(align == 'left') {
44381 if(this.labelWidth > 12){
44382 label.style = "width: " + this.labelWidth + 'px';
44384 if(this.labelWidth < 13 && this.labelmd == 0){
44385 this.labelmd = this.labelWidth;
44387 if(this.labellg > 0){
44388 label.cls += ' col-lg-' + this.labellg;
44389 input.cls += ' col-lg-' + (12 - this.labellg);
44391 if(this.labelmd > 0){
44392 label.cls += ' col-md-' + this.labelmd;
44393 container.cls += ' col-md-' + (12 - this.labelmd);
44395 if(this.labelsm > 0){
44396 label.cls += ' col-sm-' + this.labelsm;
44397 container.cls += ' col-sm-' + (12 - this.labelsm);
44399 if(this.labelxs > 0){
44400 label.cls += ' col-xs-' + this.labelxs;
44401 container.cls += ' col-xs-' + (12 - this.labelxs);
44412 var settings = this;
44414 ['xs','sm','md','lg'].map(function(size){
44415 if (settings[size]) {
44416 cfg.cls += ' col-' + size + '-' + settings[size];
44423 initEvents : function()
44425 this.indicator = this.indicatorEl();
44427 this.initCurrencyEvent();
44429 this.initNumberEvent();
44432 initCurrencyEvent : function()
44435 throw "can not find store for combo";
44438 this.store = Roo.factory(this.store, Roo.data);
44439 this.store.parent = this;
44443 this.triggerEl = this.el.select('.input-group-addon', true).first();
44445 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44450 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44451 _this.list.setWidth(lw);
44454 this.list.on('mouseover', this.onViewOver, this);
44455 this.list.on('mousemove', this.onViewMove, this);
44456 this.list.on('scroll', this.onViewScroll, this);
44459 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44462 this.view = new Roo.View(this.list, this.tpl, {
44463 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44466 this.view.on('click', this.onViewClick, this);
44468 this.store.on('beforeload', this.onBeforeLoad, this);
44469 this.store.on('load', this.onLoad, this);
44470 this.store.on('loadexception', this.onLoadException, this);
44472 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44473 "up" : function(e){
44474 this.inKeyMode = true;
44478 "down" : function(e){
44479 if(!this.isExpanded()){
44480 this.onTriggerClick();
44482 this.inKeyMode = true;
44487 "enter" : function(e){
44490 if(this.fireEvent("specialkey", this, e)){
44491 this.onViewClick(false);
44497 "esc" : function(e){
44501 "tab" : function(e){
44504 if(this.fireEvent("specialkey", this, e)){
44505 this.onViewClick(false);
44513 doRelay : function(foo, bar, hname){
44514 if(hname == 'down' || this.scope.isExpanded()){
44515 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44523 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44527 initNumberEvent : function(e)
44529 this.inputEl().on("keydown" , this.fireKey, this);
44530 this.inputEl().on("focus", this.onFocus, this);
44531 this.inputEl().on("blur", this.onBlur, this);
44533 this.inputEl().relayEvent('keyup', this);
44535 if(this.indicator){
44536 this.indicator.addClass('invisible');
44539 this.originalValue = this.getValue();
44541 if(this.validationEvent == 'keyup'){
44542 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44543 this.inputEl().on('keyup', this.filterValidation, this);
44545 else if(this.validationEvent !== false){
44546 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44549 if(this.selectOnFocus){
44550 this.on("focus", this.preFocus, this);
44553 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44554 this.inputEl().on("keypress", this.filterKeys, this);
44556 this.inputEl().relayEvent('keypress', this);
44559 var allowed = "0123456789";
44561 if(this.allowDecimals){
44562 allowed += this.decimalSeparator;
44565 if(this.allowNegative){
44569 if(this.thousandsDelimiter) {
44573 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44575 var keyPress = function(e){
44577 var k = e.getKey();
44579 var c = e.getCharCode();
44582 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44583 allowed.indexOf(String.fromCharCode(c)) === -1
44589 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44593 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44598 this.inputEl().on("keypress", keyPress, this);
44602 onTriggerClick : function(e)
44609 this.loadNext = false;
44611 if(this.isExpanded()){
44616 this.hasFocus = true;
44618 if(this.triggerAction == 'all') {
44619 this.doQuery(this.allQuery, true);
44623 this.doQuery(this.getRawValue());
44626 getCurrency : function()
44628 var v = this.currencyEl().getValue();
44633 restrictHeight : function()
44635 this.list.alignTo(this.currencyEl(), this.listAlign);
44636 this.list.alignTo(this.currencyEl(), this.listAlign);
44639 onViewClick : function(view, doFocus, el, e)
44641 var index = this.view.getSelectedIndexes()[0];
44643 var r = this.store.getAt(index);
44646 this.onSelect(r, index);
44650 onSelect : function(record, index){
44652 if(this.fireEvent('beforeselect', this, record, index) !== false){
44654 this.setFromCurrencyData(index > -1 ? record.data : false);
44658 this.fireEvent('select', this, record, index);
44662 setFromCurrencyData : function(o)
44666 this.lastCurrency = o;
44668 if (this.currencyField) {
44669 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44671 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44674 this.lastSelectionText = currency;
44676 //setting default currency
44677 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44678 this.setCurrency(this.defaultCurrency);
44682 this.setCurrency(currency);
44685 setFromData : function(o)
44689 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44691 this.setFromCurrencyData(c);
44696 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44698 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44701 this.setValue(value);
44705 setCurrency : function(v)
44707 this.currencyValue = v;
44710 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44715 setValue : function(v)
44717 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44723 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44725 this.inputEl().dom.value = (v == '') ? '' :
44726 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44728 if(!this.allowZero && v === '0') {
44729 this.hiddenEl().dom.value = '';
44730 this.inputEl().dom.value = '';
44737 getRawValue : function()
44739 var v = this.inputEl().getValue();
44744 getValue : function()
44746 return this.fixPrecision(this.parseValue(this.getRawValue()));
44749 parseValue : function(value)
44751 if(this.thousandsDelimiter) {
44753 r = new RegExp(",", "g");
44754 value = value.replace(r, "");
44757 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44758 return isNaN(value) ? '' : value;
44762 fixPrecision : function(value)
44764 if(this.thousandsDelimiter) {
44766 r = new RegExp(",", "g");
44767 value = value.replace(r, "");
44770 var nan = isNaN(value);
44772 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44773 return nan ? '' : value;
44775 return parseFloat(value).toFixed(this.decimalPrecision);
44778 decimalPrecisionFcn : function(v)
44780 return Math.floor(v);
44783 validateValue : function(value)
44785 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44789 var num = this.parseValue(value);
44792 this.markInvalid(String.format(this.nanText, value));
44796 if(num < this.minValue){
44797 this.markInvalid(String.format(this.minText, this.minValue));
44801 if(num > this.maxValue){
44802 this.markInvalid(String.format(this.maxText, this.maxValue));
44809 validate : function()
44811 if(this.disabled || this.allowBlank){
44816 var currency = this.getCurrency();
44818 if(this.validateValue(this.getRawValue()) && currency.length){
44823 this.markInvalid();
44827 getName: function()
44832 beforeBlur : function()
44838 var v = this.parseValue(this.getRawValue());
44845 onBlur : function()
44849 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44850 //this.el.removeClass(this.focusClass);
44853 this.hasFocus = false;
44855 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44859 var v = this.getValue();
44861 if(String(v) !== String(this.startValue)){
44862 this.fireEvent('change', this, v, this.startValue);
44865 this.fireEvent("blur", this);
44868 inputEl : function()
44870 return this.el.select('.roo-money-amount-input', true).first();
44873 currencyEl : function()
44875 return this.el.select('.roo-money-currency-input', true).first();
44878 hiddenEl : function()
44880 return this.el.select('input.hidden-number-input',true).first();
44884 * @class Roo.bootstrap.BezierSignature
44885 * @extends Roo.bootstrap.Component
44886 * Bootstrap BezierSignature class
44887 * This script refer to:
44888 * Title: Signature Pad
44890 * Availability: https://github.com/szimek/signature_pad
44893 * Create a new BezierSignature
44894 * @param {Object} config The config object
44897 Roo.bootstrap.BezierSignature = function(config){
44898 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44904 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44911 mouse_btn_down: true,
44914 * @cfg {int} canvas height
44916 canvas_height: '200px',
44919 * @cfg {float|function} Radius of a single dot.
44924 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44929 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44934 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44939 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44944 * @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.
44946 bg_color: 'rgba(0, 0, 0, 0)',
44949 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44951 dot_color: 'black',
44954 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44956 velocity_filter_weight: 0.7,
44959 * @cfg {function} Callback when stroke begin.
44964 * @cfg {function} Callback when stroke end.
44968 getAutoCreate : function()
44970 var cls = 'roo-signature column';
44973 cls += ' ' + this.cls;
44983 for(var i = 0; i < col_sizes.length; i++) {
44984 if(this[col_sizes[i]]) {
44985 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44995 cls: 'roo-signature-body',
44999 cls: 'roo-signature-body-canvas',
45000 height: this.canvas_height,
45001 width: this.canvas_width
45008 style: 'display: none'
45016 initEvents: function()
45018 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45020 var canvas = this.canvasEl();
45022 // mouse && touch event swapping...
45023 canvas.dom.style.touchAction = 'none';
45024 canvas.dom.style.msTouchAction = 'none';
45026 this.mouse_btn_down = false;
45027 canvas.on('mousedown', this._handleMouseDown, this);
45028 canvas.on('mousemove', this._handleMouseMove, this);
45029 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45031 if (window.PointerEvent) {
45032 canvas.on('pointerdown', this._handleMouseDown, this);
45033 canvas.on('pointermove', this._handleMouseMove, this);
45034 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45037 if ('ontouchstart' in window) {
45038 canvas.on('touchstart', this._handleTouchStart, this);
45039 canvas.on('touchmove', this._handleTouchMove, this);
45040 canvas.on('touchend', this._handleTouchEnd, this);
45043 Roo.EventManager.onWindowResize(this.resize, this, true);
45045 // file input event
45046 this.fileEl().on('change', this.uploadImage, this);
45053 resize: function(){
45055 var canvas = this.canvasEl().dom;
45056 var ctx = this.canvasElCtx();
45057 var img_data = false;
45059 if(canvas.width > 0) {
45060 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45062 // setting canvas width will clean img data
45065 var style = window.getComputedStyle ?
45066 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45068 var padding_left = parseInt(style.paddingLeft) || 0;
45069 var padding_right = parseInt(style.paddingRight) || 0;
45071 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45074 ctx.putImageData(img_data, 0, 0);
45078 _handleMouseDown: function(e)
45080 if (e.browserEvent.which === 1) {
45081 this.mouse_btn_down = true;
45082 this.strokeBegin(e);
45086 _handleMouseMove: function (e)
45088 if (this.mouse_btn_down) {
45089 this.strokeMoveUpdate(e);
45093 _handleMouseUp: function (e)
45095 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45096 this.mouse_btn_down = false;
45101 _handleTouchStart: function (e) {
45103 e.preventDefault();
45104 if (e.browserEvent.targetTouches.length === 1) {
45105 // var touch = e.browserEvent.changedTouches[0];
45106 // this.strokeBegin(touch);
45108 this.strokeBegin(e); // assume e catching the correct xy...
45112 _handleTouchMove: function (e) {
45113 e.preventDefault();
45114 // var touch = event.targetTouches[0];
45115 // _this._strokeMoveUpdate(touch);
45116 this.strokeMoveUpdate(e);
45119 _handleTouchEnd: function (e) {
45120 var wasCanvasTouched = e.target === this.canvasEl().dom;
45121 if (wasCanvasTouched) {
45122 e.preventDefault();
45123 // var touch = event.changedTouches[0];
45124 // _this._strokeEnd(touch);
45129 reset: function () {
45130 this._lastPoints = [];
45131 this._lastVelocity = 0;
45132 this._lastWidth = (this.min_width + this.max_width) / 2;
45133 this.canvasElCtx().fillStyle = this.dot_color;
45136 strokeMoveUpdate: function(e)
45138 this.strokeUpdate(e);
45140 if (this.throttle) {
45141 this.throttleStroke(this.strokeUpdate, this.throttle);
45144 this.strokeUpdate(e);
45148 strokeBegin: function(e)
45150 var newPointGroup = {
45151 color: this.dot_color,
45155 if (typeof this.onBegin === 'function') {
45159 this.curve_data.push(newPointGroup);
45161 this.strokeUpdate(e);
45164 strokeUpdate: function(e)
45166 var rect = this.canvasEl().dom.getBoundingClientRect();
45167 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45168 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45169 var lastPoints = lastPointGroup.points;
45170 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45171 var isLastPointTooClose = lastPoint
45172 ? point.distanceTo(lastPoint) <= this.min_distance
45174 var color = lastPointGroup.color;
45175 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45176 var curve = this.addPoint(point);
45178 this.drawDot({color: color, point: point});
45181 this.drawCurve({color: color, curve: curve});
45191 strokeEnd: function(e)
45193 this.strokeUpdate(e);
45194 if (typeof this.onEnd === 'function') {
45199 addPoint: function (point) {
45200 var _lastPoints = this._lastPoints;
45201 _lastPoints.push(point);
45202 if (_lastPoints.length > 2) {
45203 if (_lastPoints.length === 3) {
45204 _lastPoints.unshift(_lastPoints[0]);
45206 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45207 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45208 _lastPoints.shift();
45214 calculateCurveWidths: function (startPoint, endPoint) {
45215 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45216 (1 - this.velocity_filter_weight) * this._lastVelocity;
45218 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45221 start: this._lastWidth
45224 this._lastVelocity = velocity;
45225 this._lastWidth = newWidth;
45229 drawDot: function (_a) {
45230 var color = _a.color, point = _a.point;
45231 var ctx = this.canvasElCtx();
45232 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45234 this.drawCurveSegment(point.x, point.y, width);
45236 ctx.fillStyle = color;
45240 drawCurve: function (_a) {
45241 var color = _a.color, curve = _a.curve;
45242 var ctx = this.canvasElCtx();
45243 var widthDelta = curve.endWidth - curve.startWidth;
45244 var drawSteps = Math.floor(curve.length()) * 2;
45246 ctx.fillStyle = color;
45247 for (var i = 0; i < drawSteps; i += 1) {
45248 var t = i / drawSteps;
45254 var x = uuu * curve.startPoint.x;
45255 x += 3 * uu * t * curve.control1.x;
45256 x += 3 * u * tt * curve.control2.x;
45257 x += ttt * curve.endPoint.x;
45258 var y = uuu * curve.startPoint.y;
45259 y += 3 * uu * t * curve.control1.y;
45260 y += 3 * u * tt * curve.control2.y;
45261 y += ttt * curve.endPoint.y;
45262 var width = curve.startWidth + ttt * widthDelta;
45263 this.drawCurveSegment(x, y, width);
45269 drawCurveSegment: function (x, y, width) {
45270 var ctx = this.canvasElCtx();
45272 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45273 this.is_empty = false;
45278 var ctx = this.canvasElCtx();
45279 var canvas = this.canvasEl().dom;
45280 ctx.fillStyle = this.bg_color;
45281 ctx.clearRect(0, 0, canvas.width, canvas.height);
45282 ctx.fillRect(0, 0, canvas.width, canvas.height);
45283 this.curve_data = [];
45285 this.is_empty = true;
45290 return this.el.select('input',true).first();
45293 canvasEl: function()
45295 return this.el.select('canvas',true).first();
45298 canvasElCtx: function()
45300 return this.el.select('canvas',true).first().dom.getContext('2d');
45303 getImage: function(type)
45305 if(this.is_empty) {
45310 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45313 drawFromImage: function(img_src)
45315 var img = new Image();
45317 img.onload = function(){
45318 this.canvasElCtx().drawImage(img, 0, 0);
45323 this.is_empty = false;
45326 selectImage: function()
45328 this.fileEl().dom.click();
45331 uploadImage: function(e)
45333 var reader = new FileReader();
45335 reader.onload = function(e){
45336 var img = new Image();
45337 img.onload = function(){
45339 this.canvasElCtx().drawImage(img, 0, 0);
45341 img.src = e.target.result;
45344 reader.readAsDataURL(e.target.files[0]);
45347 // Bezier Point Constructor
45348 Point: (function () {
45349 function Point(x, y, time) {
45352 this.time = time || Date.now();
45354 Point.prototype.distanceTo = function (start) {
45355 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45357 Point.prototype.equals = function (other) {
45358 return this.x === other.x && this.y === other.y && this.time === other.time;
45360 Point.prototype.velocityFrom = function (start) {
45361 return this.time !== start.time
45362 ? this.distanceTo(start) / (this.time - start.time)
45369 // Bezier Constructor
45370 Bezier: (function () {
45371 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45372 this.startPoint = startPoint;
45373 this.control2 = control2;
45374 this.control1 = control1;
45375 this.endPoint = endPoint;
45376 this.startWidth = startWidth;
45377 this.endWidth = endWidth;
45379 Bezier.fromPoints = function (points, widths, scope) {
45380 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45381 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45382 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45384 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45385 var dx1 = s1.x - s2.x;
45386 var dy1 = s1.y - s2.y;
45387 var dx2 = s2.x - s3.x;
45388 var dy2 = s2.y - s3.y;
45389 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45390 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45391 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45392 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45393 var dxm = m1.x - m2.x;
45394 var dym = m1.y - m2.y;
45395 var k = l2 / (l1 + l2);
45396 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45397 var tx = s2.x - cm.x;
45398 var ty = s2.y - cm.y;
45400 c1: new scope.Point(m1.x + tx, m1.y + ty),
45401 c2: new scope.Point(m2.x + tx, m2.y + ty)
45404 Bezier.prototype.length = function () {
45409 for (var i = 0; i <= steps; i += 1) {
45411 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45412 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45414 var xdiff = cx - px;
45415 var ydiff = cy - py;
45416 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45423 Bezier.prototype.point = function (t, start, c1, c2, end) {
45424 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45425 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45426 + (3.0 * c2 * (1.0 - t) * t * t)
45427 + (end * t * t * t);
45432 throttleStroke: function(fn, wait) {
45433 if (wait === void 0) { wait = 250; }
45435 var timeout = null;
45439 var later = function () {
45440 previous = Date.now();
45442 result = fn.apply(storedContext, storedArgs);
45444 storedContext = null;
45448 return function wrapper() {
45450 for (var _i = 0; _i < arguments.length; _i++) {
45451 args[_i] = arguments[_i];
45453 var now = Date.now();
45454 var remaining = wait - (now - previous);
45455 storedContext = this;
45457 if (remaining <= 0 || remaining > wait) {
45459 clearTimeout(timeout);
45463 result = fn.apply(storedContext, storedArgs);
45465 storedContext = null;
45469 else if (!timeout) {
45470 timeout = window.setTimeout(later, remaining);