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 * @cfg {Boolean} loadMask (true|false) default false
8725 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8726 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8727 * @cfg {Boolean} rowSelection (true|false) default false
8728 * @cfg {Boolean} cellSelection (true|false) default false
8729 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8730 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8731 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8732 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8733 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8734 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8737 * Create a new Table
8738 * @param {Object} config The config object
8741 Roo.bootstrap.Table = function(config)
8743 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8746 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8747 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8748 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8749 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8751 this.view = this; // compat with grid.
8753 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8755 this.sm.grid = this;
8756 this.selModel = Roo.factory(this.sm, Roo.grid);
8757 this.sm = this.selModel;
8758 this.sm.xmodule = this.xmodule || false;
8761 if (this.cm && typeof(this.cm.config) == 'undefined') {
8762 this.colModel = new Roo.grid.ColumnModel(this.cm);
8763 this.cm = this.colModel;
8764 this.cm.xmodule = this.xmodule || false;
8767 this.store= Roo.factory(this.store, Roo.data);
8768 this.ds = this.store;
8769 this.ds.xmodule = this.xmodule || false;
8772 if (this.footer && this.store) {
8773 this.footer.dataSource = this.ds;
8774 this.footer = Roo.factory(this.footer);
8781 * Fires when a cell is clicked
8782 * @param {Roo.bootstrap.Table} this
8783 * @param {Roo.Element} el
8784 * @param {Number} rowIndex
8785 * @param {Number} columnIndex
8786 * @param {Roo.EventObject} e
8790 * @event celldblclick
8791 * Fires when a cell is double clicked
8792 * @param {Roo.bootstrap.Table} this
8793 * @param {Roo.Element} el
8794 * @param {Number} rowIndex
8795 * @param {Number} columnIndex
8796 * @param {Roo.EventObject} e
8798 "celldblclick" : true,
8801 * Fires when a row is clicked
8802 * @param {Roo.bootstrap.Table} this
8803 * @param {Roo.Element} el
8804 * @param {Number} rowIndex
8805 * @param {Roo.EventObject} e
8809 * @event rowdblclick
8810 * Fires when a row is double clicked
8811 * @param {Roo.bootstrap.Table} this
8812 * @param {Roo.Element} el
8813 * @param {Number} rowIndex
8814 * @param {Roo.EventObject} e
8816 "rowdblclick" : true,
8819 * Fires when a mouseover occur
8820 * @param {Roo.bootstrap.Table} this
8821 * @param {Roo.Element} el
8822 * @param {Number} rowIndex
8823 * @param {Number} columnIndex
8824 * @param {Roo.EventObject} e
8829 * Fires when a mouseout occur
8830 * @param {Roo.bootstrap.Table} this
8831 * @param {Roo.Element} el
8832 * @param {Number} rowIndex
8833 * @param {Number} columnIndex
8834 * @param {Roo.EventObject} e
8839 * Fires when a row is rendered, so you can change add a style to it.
8840 * @param {Roo.bootstrap.Table} this
8841 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8845 * @event rowsrendered
8846 * Fires when all the rows have been rendered
8847 * @param {Roo.bootstrap.Table} this
8849 'rowsrendered' : true,
8851 * @event contextmenu
8852 * The raw contextmenu event for the entire grid.
8853 * @param {Roo.EventObject} e
8855 "contextmenu" : true,
8857 * @event rowcontextmenu
8858 * Fires when a row is right clicked
8859 * @param {Roo.bootstrap.Table} this
8860 * @param {Number} rowIndex
8861 * @param {Roo.EventObject} e
8863 "rowcontextmenu" : true,
8865 * @event cellcontextmenu
8866 * Fires when a cell is right clicked
8867 * @param {Roo.bootstrap.Table} this
8868 * @param {Number} rowIndex
8869 * @param {Number} cellIndex
8870 * @param {Roo.EventObject} e
8872 "cellcontextmenu" : true,
8874 * @event headercontextmenu
8875 * Fires when a header is right clicked
8876 * @param {Roo.bootstrap.Table} this
8877 * @param {Number} columnIndex
8878 * @param {Roo.EventObject} e
8880 "headercontextmenu" : true,
8883 * The raw mousedown event for the entire grid.
8884 * @param {Roo.EventObject} e
8891 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8907 enableColumnResize: true,
8909 rowSelection : false,
8910 cellSelection : false,
8913 minColumnWidth : 50,
8915 // Roo.Element - the tbody
8916 bodyEl: false, // <tbody> Roo.Element - thead element
8917 headEl: false, // <thead> Roo.Element - thead element
8918 resizeProxy : false, // proxy element for dragging?
8922 container: false, // used by gridpanel...
8928 auto_hide_footer : false,
8930 view: false, // actually points to this..
8932 getAutoCreate : function()
8934 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8941 // this get's auto added by panel.Grid
8942 if (this.scrollBody) {
8943 cfg.cls += ' table-body-fixed';
8946 cfg.cls += ' table-striped';
8950 cfg.cls += ' table-hover';
8952 if (this.bordered) {
8953 cfg.cls += ' table-bordered';
8955 if (this.condensed) {
8956 cfg.cls += ' table-condensed';
8959 if (this.responsive) {
8960 cfg.cls += ' table-responsive';
8964 cfg.cls+= ' ' +this.cls;
8970 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8973 if(this.store || this.cm){
8974 if(this.headerShow){
8975 cfg.cn.push(this.renderHeader());
8978 cfg.cn.push(this.renderBody());
8980 if(this.footerShow){
8981 cfg.cn.push(this.renderFooter());
8983 // where does this come from?
8984 //cfg.cls+= ' TableGrid';
8987 return { cn : [ cfg ] };
8990 initEvents : function()
8992 if(!this.store || !this.cm){
8995 if (this.selModel) {
8996 this.selModel.initEvents();
9000 //Roo.log('initEvents with ds!!!!');
9002 this.bodyEl = this.el.select('tbody', true).first();
9003 this.headEl = this.el.select('thead', true).first();
9004 this.mainFoot = this.el.select('tfoot', true).first();
9009 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9010 e.on('click', this.sort, this);
9014 // why is this done????? = it breaks dialogs??
9015 //this.parent().el.setStyle('position', 'relative');
9019 this.footer.parentId = this.id;
9020 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9023 this.el.select('tfoot tr td').first().addClass('hide');
9028 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9031 this.store.on('load', this.onLoad, this);
9032 this.store.on('beforeload', this.onBeforeLoad, this);
9033 this.store.on('update', this.onUpdate, this);
9034 this.store.on('add', this.onAdd, this);
9035 this.store.on("clear", this.clear, this);
9037 this.el.on("contextmenu", this.onContextMenu, this);
9040 this.cm.on("headerchange", this.onHeaderChange, this);
9041 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9043 //?? does bodyEl get replaced on render?
9044 this.bodyEl.on("click", this.onClick, this);
9045 this.bodyEl.on("dblclick", this.onDblClick, this);
9046 this.bodyEl.on('scroll', this.onBodyScroll, this);
9048 // guessing mainbody will work - this relays usually caught by selmodel at present.
9049 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9052 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9055 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9056 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9061 // Compatibility with grid - we implement all the view features at present.
9062 getView : function()
9067 initCSS : function()
9071 var cm = this.cm, styles = [];
9072 this.CSS.removeStyleSheet(this.id + '-cssrules');
9073 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9074 // we can honour xs/sm/md/xl as widths...
9075 // we first have to decide what widht we are currently at...
9076 var sz = Roo.getGridSize();
9080 var cols = []; // visable cols.
9082 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9083 var w = cm.getColumnWidth(i, false);
9085 cols.push( { rel : false, abs : 0 });
9089 cols.push( { rel : false, abs : w });
9091 last = i; // not really..
9094 var w = cm.getColumnWidth(i, sz);
9099 cols.push( { rel : w, abs : false });
9102 var avail = this.bodyEl.dom.clientWidth - total_abs;
9104 var unitWidth = Math.floor(avail / total);
9105 var rem = avail - (unitWidth * total);
9107 var hidden, width, pos = 0 , splithide , left;
9108 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9110 hidden = 'display:none;';
9112 width = 'width:0px;';
9114 if(!cm.isHidden(i)){
9118 // we can honour xs/sm/md/xl ?
9119 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9121 hidden = 'display:none;';
9123 // width should return a small number...
9125 w+=rem; // add the remaining with..
9128 left = "left:" + (pos -4) + "px;";
9129 width = "width:" + w+ "px;";
9132 if (this.responsive) {
9135 hidden = cm.isHidden(i) ? 'display:none' : '';
9136 splithide = 'display: none';
9139 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9142 splithide = 'display:none;';
9145 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9146 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9151 //Roo.log(styles.join(''));
9152 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9158 onContextMenu : function(e, t)
9160 this.processEvent("contextmenu", e);
9163 processEvent : function(name, e)
9165 if (name != 'touchstart' ) {
9166 this.fireEvent(name, e);
9169 var t = e.getTarget();
9171 var cell = Roo.get(t);
9177 if(cell.findParent('tfoot', false, true)){
9181 if(cell.findParent('thead', false, true)){
9183 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9184 cell = Roo.get(t).findParent('th', false, true);
9186 Roo.log("failed to find th in thead?");
9187 Roo.log(e.getTarget());
9192 var cellIndex = cell.dom.cellIndex;
9194 var ename = name == 'touchstart' ? 'click' : name;
9195 this.fireEvent("header" + ename, this, cellIndex, e);
9200 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9201 cell = Roo.get(t).findParent('td', false, true);
9203 Roo.log("failed to find th in tbody?");
9204 Roo.log(e.getTarget());
9209 var row = cell.findParent('tr', false, true);
9210 var cellIndex = cell.dom.cellIndex;
9211 var rowIndex = row.dom.rowIndex - 1;
9215 this.fireEvent("row" + name, this, rowIndex, e);
9219 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9225 onMouseover : function(e, el)
9227 var cell = Roo.get(el);
9233 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9234 cell = cell.findParent('td', false, true);
9237 var row = cell.findParent('tr', false, true);
9238 var cellIndex = cell.dom.cellIndex;
9239 var rowIndex = row.dom.rowIndex - 1; // start from 0
9241 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9245 onMouseout : function(e, el)
9247 var cell = Roo.get(el);
9253 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9254 cell = cell.findParent('td', false, true);
9257 var row = cell.findParent('tr', false, true);
9258 var cellIndex = cell.dom.cellIndex;
9259 var rowIndex = row.dom.rowIndex - 1; // start from 0
9261 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9265 onClick : function(e, el)
9267 var cell = Roo.get(el);
9269 if(!cell || (!this.cellSelection && !this.rowSelection)){
9273 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9274 cell = cell.findParent('td', false, true);
9277 if(!cell || typeof(cell) == 'undefined'){
9281 var row = cell.findParent('tr', false, true);
9283 if(!row || typeof(row) == 'undefined'){
9287 var cellIndex = cell.dom.cellIndex;
9288 var rowIndex = this.getRowIndex(row);
9290 // why??? - should these not be based on SelectionModel?
9291 //if(this.cellSelection){
9292 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9295 //if(this.rowSelection){
9296 this.fireEvent('rowclick', this, row, rowIndex, e);
9301 onDblClick : function(e,el)
9303 var cell = Roo.get(el);
9305 if(!cell || (!this.cellSelection && !this.rowSelection)){
9309 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9310 cell = cell.findParent('td', false, true);
9313 if(!cell || typeof(cell) == 'undefined'){
9317 var row = cell.findParent('tr', false, true);
9319 if(!row || typeof(row) == 'undefined'){
9323 var cellIndex = cell.dom.cellIndex;
9324 var rowIndex = this.getRowIndex(row);
9326 if(this.cellSelection){
9327 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9330 if(this.rowSelection){
9331 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9334 findRowIndex : function(el)
9336 var cell = Roo.get(el);
9340 var row = cell.findParent('tr', false, true);
9342 if(!row || typeof(row) == 'undefined'){
9345 return this.getRowIndex(row);
9347 sort : function(e,el)
9349 var col = Roo.get(el);
9351 if(!col.hasClass('sortable')){
9355 var sort = col.attr('sort');
9358 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9362 this.store.sortInfo = {field : sort, direction : dir};
9365 Roo.log("calling footer first");
9366 this.footer.onClick('first');
9369 this.store.load({ params : { start : 0 } });
9373 renderHeader : function()
9381 this.totalWidth = 0;
9383 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9385 var config = cm.config[i];
9389 cls : 'x-hcol-' + i,
9392 html: cm.getColumnHeader(i)
9395 var tooltip = cm.getColumnTooltip(i);
9397 c.tooltip = tooltip;
9403 if(typeof(config.sortable) != 'undefined' && config.sortable){
9404 c.cls += ' sortable';
9405 c.html = '<i class="fa"></i>' + c.html;
9408 // could use BS4 hidden-..-down
9410 if(typeof(config.lgHeader) != 'undefined'){
9411 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9414 if(typeof(config.mdHeader) != 'undefined'){
9415 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9418 if(typeof(config.smHeader) != 'undefined'){
9419 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9422 if(typeof(config.xsHeader) != 'undefined'){
9423 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9430 if(typeof(config.tooltip) != 'undefined'){
9431 c.tooltip = config.tooltip;
9434 if(typeof(config.colspan) != 'undefined'){
9435 c.colspan = config.colspan;
9438 // hidden is handled by CSS now
9440 if(typeof(config.dataIndex) != 'undefined'){
9441 c.sort = config.dataIndex;
9446 if(typeof(config.align) != 'undefined' && config.align.length){
9447 c.style += ' text-align:' + config.align + ';';
9450 /* width is done in CSS
9451 *if(typeof(config.width) != 'undefined'){
9452 c.style += ' width:' + config.width + 'px;';
9453 this.totalWidth += config.width;
9455 this.totalWidth += 100; // assume minimum of 100 per column?
9459 if(typeof(config.cls) != 'undefined'){
9460 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9462 // this is the bit that doesnt reall work at all...
9464 if (this.responsive) {
9467 ['xs','sm','md','lg'].map(function(size){
9469 if(typeof(config[size]) == 'undefined'){
9473 if (!config[size]) { // 0 = hidden
9474 // BS 4 '0' is treated as hide that column and below.
9475 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9479 c.cls += ' col-' + size + '-' + config[size] + (
9480 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9488 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9499 renderBody : function()
9509 colspan : this.cm.getColumnCount()
9519 renderFooter : function()
9529 colspan : this.cm.getColumnCount()
9543 // Roo.log('ds onload');
9548 var ds = this.store;
9550 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9551 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9552 if (_this.store.sortInfo) {
9554 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9555 e.select('i', true).addClass(['fa-arrow-up']);
9558 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9559 e.select('i', true).addClass(['fa-arrow-down']);
9564 var tbody = this.bodyEl;
9566 if(ds.getCount() > 0){
9567 ds.data.each(function(d,rowIndex){
9568 var row = this.renderRow(cm, ds, rowIndex);
9570 tbody.createChild(row);
9574 if(row.cellObjects.length){
9575 Roo.each(row.cellObjects, function(r){
9576 _this.renderCellObject(r);
9583 var tfoot = this.el.select('tfoot', true).first();
9585 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9587 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9589 var total = this.ds.getTotalCount();
9591 if(this.footer.pageSize < total){
9592 this.mainFoot.show();
9596 Roo.each(this.el.select('tbody td', true).elements, function(e){
9597 e.on('mouseover', _this.onMouseover, _this);
9600 Roo.each(this.el.select('tbody td', true).elements, function(e){
9601 e.on('mouseout', _this.onMouseout, _this);
9603 this.fireEvent('rowsrendered', this);
9607 this.initCSS(); /// resize cols
9613 onUpdate : function(ds,record)
9615 this.refreshRow(record);
9619 onRemove : function(ds, record, index, isUpdate){
9620 if(isUpdate !== true){
9621 this.fireEvent("beforerowremoved", this, index, record);
9623 var bt = this.bodyEl.dom;
9625 var rows = this.el.select('tbody > tr', true).elements;
9627 if(typeof(rows[index]) != 'undefined'){
9628 bt.removeChild(rows[index].dom);
9631 // if(bt.rows[index]){
9632 // bt.removeChild(bt.rows[index]);
9635 if(isUpdate !== true){
9636 //this.stripeRows(index);
9637 //this.syncRowHeights(index, index);
9639 this.fireEvent("rowremoved", this, index, record);
9643 onAdd : function(ds, records, rowIndex)
9645 //Roo.log('on Add called');
9646 // - note this does not handle multiple adding very well..
9647 var bt = this.bodyEl.dom;
9648 for (var i =0 ; i < records.length;i++) {
9649 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9650 //Roo.log(records[i]);
9651 //Roo.log(this.store.getAt(rowIndex+i));
9652 this.insertRow(this.store, rowIndex + i, false);
9659 refreshRow : function(record){
9660 var ds = this.store, index;
9661 if(typeof record == 'number'){
9663 record = ds.getAt(index);
9665 index = ds.indexOf(record);
9667 return; // should not happen - but seems to
9670 this.insertRow(ds, index, true);
9672 this.onRemove(ds, record, index+1, true);
9674 //this.syncRowHeights(index, index);
9676 this.fireEvent("rowupdated", this, index, record);
9678 // private - called by RowSelection
9679 onRowSelect : function(rowIndex){
9680 var row = this.getRowDom(rowIndex);
9681 row.addClass(['bg-info','info']);
9683 // private - called by RowSelection
9684 onRowDeselect : function(rowIndex)
9689 var row = this.getRowDom(rowIndex);
9690 row.removeClass(['bg-info','info']);
9693 * Focuses the specified row.
9694 * @param {Number} row The row index
9696 focusRow : function(row)
9698 //Roo.log('GridView.focusRow');
9699 var x = this.bodyEl.dom.scrollLeft;
9700 this.focusCell(row, 0, false);
9701 this.bodyEl.dom.scrollLeft = x;
9705 * Focuses the specified cell.
9706 * @param {Number} row The row index
9707 * @param {Number} col The column index
9708 * @param {Boolean} hscroll false to disable horizontal scrolling
9710 focusCell : function(row, col, hscroll)
9712 //Roo.log('GridView.focusCell');
9713 var el = this.ensureVisible(row, col, hscroll);
9714 // not sure what focusEL achives = it's a <a> pos relative
9715 //this.focusEl.alignTo(el, "tl-tl");
9717 // this.focusEl.focus();
9719 // this.focusEl.focus.defer(1, this.focusEl);
9724 * Scrolls the specified cell into view
9725 * @param {Number} row The row index
9726 * @param {Number} col The column index
9727 * @param {Boolean} hscroll false to disable horizontal scrolling
9729 ensureVisible : function(row, col, hscroll)
9731 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9732 //return null; //disable for testing.
9733 if(typeof row != "number"){
9736 if(row < 0 && row >= this.ds.getCount()){
9739 col = (col !== undefined ? col : 0);
9741 while(cm.isHidden(col)){
9745 var el = this.getCellDom(row, col);
9749 var c = this.bodyEl.dom;
9751 var ctop = parseInt(el.offsetTop, 10);
9752 var cleft = parseInt(el.offsetLeft, 10);
9753 var cbot = ctop + el.offsetHeight;
9754 var cright = cleft + el.offsetWidth;
9756 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9757 var ch = 0; //?? header is not withing the area?
9758 var stop = parseInt(c.scrollTop, 10);
9759 var sleft = parseInt(c.scrollLeft, 10);
9760 var sbot = stop + ch;
9761 var sright = sleft + c.clientWidth;
9763 Roo.log('GridView.ensureVisible:' +
9765 ' c.clientHeight:' + c.clientHeight +
9766 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9775 //Roo.log("set scrolltop to ctop DISABLE?");
9776 }else if(cbot > sbot){
9777 //Roo.log("set scrolltop to cbot-ch");
9778 c.scrollTop = cbot-ch;
9781 if(hscroll !== false){
9783 c.scrollLeft = cleft;
9784 }else if(cright > sright){
9785 c.scrollLeft = cright-c.clientWidth;
9793 insertRow : function(dm, rowIndex, isUpdate){
9796 this.fireEvent("beforerowsinserted", this, rowIndex);
9798 //var s = this.getScrollState();
9799 var row = this.renderRow(this.cm, this.store, rowIndex);
9800 // insert before rowIndex..
9801 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9805 if(row.cellObjects.length){
9806 Roo.each(row.cellObjects, function(r){
9807 _this.renderCellObject(r);
9812 this.fireEvent("rowsinserted", this, rowIndex);
9813 //this.syncRowHeights(firstRow, lastRow);
9814 //this.stripeRows(firstRow);
9821 getRowDom : function(rowIndex)
9823 var rows = this.el.select('tbody > tr', true).elements;
9825 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9828 getCellDom : function(rowIndex, colIndex)
9830 var row = this.getRowDom(rowIndex);
9831 if (row === false) {
9834 var cols = row.select('td', true).elements;
9835 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9839 // returns the object tree for a tr..
9842 renderRow : function(cm, ds, rowIndex)
9844 var d = ds.getAt(rowIndex);
9848 cls : 'x-row-' + rowIndex,
9852 var cellObjects = [];
9854 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9855 var config = cm.config[i];
9857 var renderer = cm.getRenderer(i);
9861 if(typeof(renderer) !== 'undefined'){
9862 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9864 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9865 // and are rendered into the cells after the row is rendered - using the id for the element.
9867 if(typeof(value) === 'object'){
9877 rowIndex : rowIndex,
9882 this.fireEvent('rowclass', this, rowcfg);
9886 // this might end up displaying HTML?
9887 // this is too messy... - better to only do it on columsn you know are going to be too long
9888 //tooltip : (typeof(value) === 'object') ? '' : value,
9889 cls : rowcfg.rowClass + ' x-col-' + i,
9891 html: (typeof(value) === 'object') ? '' : value
9898 if(typeof(config.colspan) != 'undefined'){
9899 td.colspan = config.colspan;
9904 if(typeof(config.align) != 'undefined' && config.align.length){
9905 td.style += ' text-align:' + config.align + ';';
9907 if(typeof(config.valign) != 'undefined' && config.valign.length){
9908 td.style += ' vertical-align:' + config.valign + ';';
9911 if(typeof(config.width) != 'undefined'){
9912 td.style += ' width:' + config.width + 'px;';
9916 if(typeof(config.cursor) != 'undefined'){
9917 td.style += ' cursor:' + config.cursor + ';';
9920 if(typeof(config.cls) != 'undefined'){
9921 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9923 if (this.responsive) {
9924 ['xs','sm','md','lg'].map(function(size){
9926 if(typeof(config[size]) == 'undefined'){
9932 if (!config[size]) { // 0 = hidden
9933 // BS 4 '0' is treated as hide that column and below.
9934 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9938 td.cls += ' col-' + size + '-' + config[size] + (
9939 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9949 row.cellObjects = cellObjects;
9957 onBeforeLoad : function()
9966 this.el.select('tbody', true).first().dom.innerHTML = '';
9969 * Show or hide a row.
9970 * @param {Number} rowIndex to show or hide
9971 * @param {Boolean} state hide
9973 setRowVisibility : function(rowIndex, state)
9975 var bt = this.bodyEl.dom;
9977 var rows = this.el.select('tbody > tr', true).elements;
9979 if(typeof(rows[rowIndex]) == 'undefined'){
9982 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9987 getSelectionModel : function(){
9989 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9991 return this.selModel;
9994 * Render the Roo.bootstrap object from renderder
9996 renderCellObject : function(r)
10000 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10002 var t = r.cfg.render(r.container);
10005 Roo.each(r.cfg.cn, function(c){
10007 container: t.getChildContainer(),
10010 _this.renderCellObject(child);
10015 * get the Row Index from a dom element.
10016 * @param {Roo.Element} row The row to look for
10017 * @returns {Number} the row
10019 getRowIndex : function(row)
10023 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10034 * get the header TH element for columnIndex
10035 * @param {Number} columnIndex
10036 * @returns {Roo.Element}
10038 getHeaderIndex: function(colIndex)
10040 var cols = this.headEl.select('th', true).elements;
10041 return cols[colIndex];
10044 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10045 * @param {domElement} cell to look for
10046 * @returns {Number} the column
10048 getCellIndex : function(cell)
10050 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10052 return parseInt(id[1], 10);
10057 * Returns the grid's underlying element = used by panel.Grid
10058 * @return {Element} The element
10060 getGridEl : function(){
10064 * Forces a resize - used by panel.Grid
10065 * @return {Element} The element
10067 autoSize : function()
10069 //var ctr = Roo.get(this.container.dom.parentElement);
10070 var ctr = Roo.get(this.el.dom);
10072 var thd = this.getGridEl().select('thead',true).first();
10073 var tbd = this.getGridEl().select('tbody', true).first();
10074 var tfd = this.getGridEl().select('tfoot', true).first();
10076 var cw = ctr.getWidth();
10077 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10081 tbd.setWidth(ctr.getWidth());
10082 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10083 // this needs fixing for various usage - currently only hydra job advers I think..
10085 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10087 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10090 cw = Math.max(cw, this.totalWidth);
10091 this.getGridEl().select('tbody tr',true).setWidth(cw);
10094 // resize 'expandable coloumn?
10096 return; // we doe not have a view in this design..
10099 onBodyScroll: function()
10101 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10103 this.headEl.setStyle({
10104 'position' : 'relative',
10105 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10111 var scrollHeight = this.bodyEl.dom.scrollHeight;
10113 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10115 var height = this.bodyEl.getHeight();
10117 if(scrollHeight - height == scrollTop) {
10119 var total = this.ds.getTotalCount();
10121 if(this.footer.cursor + this.footer.pageSize < total){
10123 this.footer.ds.load({
10125 start : this.footer.cursor + this.footer.pageSize,
10126 limit : this.footer.pageSize
10135 onColumnSplitterMoved : function(i, diff)
10137 this.userResized = true;
10139 var cm = this.colModel;
10141 var w = this.getHeaderIndex(i).getWidth() + diff;
10144 cm.setColumnWidth(i, w, true);
10146 //var cid = cm.getColumnId(i); << not used in this version?
10147 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10149 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10150 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10151 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10153 //this.updateSplitters();
10154 //this.layout(); << ??
10155 this.fireEvent("columnresize", i, w);
10157 onHeaderChange : function()
10159 var header = this.renderHeader();
10160 var table = this.el.select('table', true).first();
10162 this.headEl.remove();
10163 this.headEl = table.createChild(header, this.bodyEl, false);
10165 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10166 e.on('click', this.sort, this);
10169 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10170 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10175 onHiddenChange : function(colModel, colIndex, hidden)
10178 this.cm.setHidden()
10179 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10180 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10182 this.CSS.updateRule(thSelector, "display", "");
10183 this.CSS.updateRule(tdSelector, "display", "");
10186 this.CSS.updateRule(thSelector, "display", "none");
10187 this.CSS.updateRule(tdSelector, "display", "none");
10190 // onload calls initCSS()
10191 this.onHeaderChange();
10195 setColumnWidth: function(col_index, width)
10197 // width = "md-2 xs-2..."
10198 if(!this.colModel.config[col_index]) {
10202 var w = width.split(" ");
10204 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10206 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10209 for(var j = 0; j < w.length; j++) {
10215 var size_cls = w[j].split("-");
10217 if(!Number.isInteger(size_cls[1] * 1)) {
10221 if(!this.colModel.config[col_index][size_cls[0]]) {
10225 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10229 h_row[0].classList.replace(
10230 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10231 "col-"+size_cls[0]+"-"+size_cls[1]
10234 for(var i = 0; i < rows.length; i++) {
10236 var size_cls = w[j].split("-");
10238 if(!Number.isInteger(size_cls[1] * 1)) {
10242 if(!this.colModel.config[col_index][size_cls[0]]) {
10246 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10250 rows[i].classList.replace(
10251 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10252 "col-"+size_cls[0]+"-"+size_cls[1]
10256 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10261 // currently only used to find the split on drag..
10262 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10267 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10268 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10277 * @class Roo.bootstrap.TableCell
10278 * @extends Roo.bootstrap.Component
10279 * Bootstrap TableCell class
10280 * @cfg {String} html cell contain text
10281 * @cfg {String} cls cell class
10282 * @cfg {String} tag cell tag (td|th) default td
10283 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10284 * @cfg {String} align Aligns the content in a cell
10285 * @cfg {String} axis Categorizes cells
10286 * @cfg {String} bgcolor Specifies the background color of a cell
10287 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10288 * @cfg {Number} colspan Specifies the number of columns a cell should span
10289 * @cfg {String} headers Specifies one or more header cells a cell is related to
10290 * @cfg {Number} height Sets the height of a cell
10291 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10292 * @cfg {Number} rowspan Sets the number of rows a cell should span
10293 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10294 * @cfg {String} valign Vertical aligns the content in a cell
10295 * @cfg {Number} width Specifies the width of a cell
10298 * Create a new TableCell
10299 * @param {Object} config The config object
10302 Roo.bootstrap.TableCell = function(config){
10303 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10306 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10326 getAutoCreate : function(){
10327 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10334 cfg.tag = this.tag;
10347 cfg.align=this.align
10352 if (this.bgcolor) {
10353 cfg.bgcolor=this.bgcolor
10355 if (this.charoff) {
10356 cfg.charoff=this.charoff
10358 if (this.colspan) {
10359 cfg.colspan=this.colspan
10361 if (this.headers) {
10362 cfg.headers=this.headers
10365 cfg.height=this.height
10368 cfg.nowrap=this.nowrap
10370 if (this.rowspan) {
10371 cfg.rowspan=this.rowspan
10374 cfg.scope=this.scope
10377 cfg.valign=this.valign
10380 cfg.width=this.width
10399 * @class Roo.bootstrap.TableRow
10400 * @extends Roo.bootstrap.Component
10401 * Bootstrap TableRow class
10402 * @cfg {String} cls row class
10403 * @cfg {String} align Aligns the content in a table row
10404 * @cfg {String} bgcolor Specifies a background color for a table row
10405 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10406 * @cfg {String} valign Vertical aligns the content in a table row
10409 * Create a new TableRow
10410 * @param {Object} config The config object
10413 Roo.bootstrap.TableRow = function(config){
10414 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10417 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10425 getAutoCreate : function(){
10426 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10433 cfg.cls = this.cls;
10436 cfg.align = this.align;
10439 cfg.bgcolor = this.bgcolor;
10442 cfg.charoff = this.charoff;
10445 cfg.valign = this.valign;
10463 * @class Roo.bootstrap.TableBody
10464 * @extends Roo.bootstrap.Component
10465 * Bootstrap TableBody class
10466 * @cfg {String} cls element class
10467 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10468 * @cfg {String} align Aligns the content inside the element
10469 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10470 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10473 * Create a new TableBody
10474 * @param {Object} config The config object
10477 Roo.bootstrap.TableBody = function(config){
10478 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10481 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10489 getAutoCreate : function(){
10490 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10500 cfg.tag = this.tag;
10504 cfg.align = this.align;
10507 cfg.charoff = this.charoff;
10510 cfg.valign = this.valign;
10517 // initEvents : function()
10520 // if(!this.store){
10524 // this.store = Roo.factory(this.store, Roo.data);
10525 // this.store.on('load', this.onLoad, this);
10527 // this.store.load();
10531 // onLoad: function ()
10533 // this.fireEvent('load', this);
10543 * Ext JS Library 1.1.1
10544 * Copyright(c) 2006-2007, Ext JS, LLC.
10546 * Originally Released Under LGPL - original licence link has changed is not relivant.
10549 * <script type="text/javascript">
10552 // as we use this in bootstrap.
10553 Roo.namespace('Roo.form');
10555 * @class Roo.form.Action
10556 * Internal Class used to handle form actions
10558 * @param {Roo.form.BasicForm} el The form element or its id
10559 * @param {Object} config Configuration options
10564 // define the action interface
10565 Roo.form.Action = function(form, options){
10567 this.options = options || {};
10570 * Client Validation Failed
10573 Roo.form.Action.CLIENT_INVALID = 'client';
10575 * Server Validation Failed
10578 Roo.form.Action.SERVER_INVALID = 'server';
10580 * Connect to Server Failed
10583 Roo.form.Action.CONNECT_FAILURE = 'connect';
10585 * Reading Data from Server Failed
10588 Roo.form.Action.LOAD_FAILURE = 'load';
10590 Roo.form.Action.prototype = {
10592 failureType : undefined,
10593 response : undefined,
10594 result : undefined,
10596 // interface method
10597 run : function(options){
10601 // interface method
10602 success : function(response){
10606 // interface method
10607 handleResponse : function(response){
10611 // default connection failure
10612 failure : function(response){
10614 this.response = response;
10615 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10616 this.form.afterAction(this, false);
10619 processResponse : function(response){
10620 this.response = response;
10621 if(!response.responseText){
10624 this.result = this.handleResponse(response);
10625 return this.result;
10628 // utility functions used internally
10629 getUrl : function(appendParams){
10630 var url = this.options.url || this.form.url || this.form.el.dom.action;
10632 var p = this.getParams();
10634 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10640 getMethod : function(){
10641 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10644 getParams : function(){
10645 var bp = this.form.baseParams;
10646 var p = this.options.params;
10648 if(typeof p == "object"){
10649 p = Roo.urlEncode(Roo.applyIf(p, bp));
10650 }else if(typeof p == 'string' && bp){
10651 p += '&' + Roo.urlEncode(bp);
10654 p = Roo.urlEncode(bp);
10659 createCallback : function(){
10661 success: this.success,
10662 failure: this.failure,
10664 timeout: (this.form.timeout*1000),
10665 upload: this.form.fileUpload ? this.success : undefined
10670 Roo.form.Action.Submit = function(form, options){
10671 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10674 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10677 haveProgress : false,
10678 uploadComplete : false,
10680 // uploadProgress indicator.
10681 uploadProgress : function()
10683 if (!this.form.progressUrl) {
10687 if (!this.haveProgress) {
10688 Roo.MessageBox.progress("Uploading", "Uploading");
10690 if (this.uploadComplete) {
10691 Roo.MessageBox.hide();
10695 this.haveProgress = true;
10697 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10699 var c = new Roo.data.Connection();
10701 url : this.form.progressUrl,
10706 success : function(req){
10707 //console.log(data);
10711 rdata = Roo.decode(req.responseText)
10713 Roo.log("Invalid data from server..");
10717 if (!rdata || !rdata.success) {
10719 Roo.MessageBox.alert(Roo.encode(rdata));
10722 var data = rdata.data;
10724 if (this.uploadComplete) {
10725 Roo.MessageBox.hide();
10730 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10731 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10734 this.uploadProgress.defer(2000,this);
10737 failure: function(data) {
10738 Roo.log('progress url failed ');
10749 // run get Values on the form, so it syncs any secondary forms.
10750 this.form.getValues();
10752 var o = this.options;
10753 var method = this.getMethod();
10754 var isPost = method == 'POST';
10755 if(o.clientValidation === false || this.form.isValid()){
10757 if (this.form.progressUrl) {
10758 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10759 (new Date() * 1) + '' + Math.random());
10764 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10765 form:this.form.el.dom,
10766 url:this.getUrl(!isPost),
10768 params:isPost ? this.getParams() : null,
10769 isUpload: this.form.fileUpload,
10770 formData : this.form.formData
10773 this.uploadProgress();
10775 }else if (o.clientValidation !== false){ // client validation failed
10776 this.failureType = Roo.form.Action.CLIENT_INVALID;
10777 this.form.afterAction(this, false);
10781 success : function(response)
10783 this.uploadComplete= true;
10784 if (this.haveProgress) {
10785 Roo.MessageBox.hide();
10789 var result = this.processResponse(response);
10790 if(result === true || result.success){
10791 this.form.afterAction(this, true);
10795 this.form.markInvalid(result.errors);
10796 this.failureType = Roo.form.Action.SERVER_INVALID;
10798 this.form.afterAction(this, false);
10800 failure : function(response)
10802 this.uploadComplete= true;
10803 if (this.haveProgress) {
10804 Roo.MessageBox.hide();
10807 this.response = response;
10808 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10809 this.form.afterAction(this, false);
10812 handleResponse : function(response){
10813 if(this.form.errorReader){
10814 var rs = this.form.errorReader.read(response);
10817 for(var i = 0, len = rs.records.length; i < len; i++) {
10818 var r = rs.records[i];
10819 errors[i] = r.data;
10822 if(errors.length < 1){
10826 success : rs.success,
10832 ret = Roo.decode(response.responseText);
10836 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10846 Roo.form.Action.Load = function(form, options){
10847 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10848 this.reader = this.form.reader;
10851 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10856 Roo.Ajax.request(Roo.apply(
10857 this.createCallback(), {
10858 method:this.getMethod(),
10859 url:this.getUrl(false),
10860 params:this.getParams()
10864 success : function(response){
10866 var result = this.processResponse(response);
10867 if(result === true || !result.success || !result.data){
10868 this.failureType = Roo.form.Action.LOAD_FAILURE;
10869 this.form.afterAction(this, false);
10872 this.form.clearInvalid();
10873 this.form.setValues(result.data);
10874 this.form.afterAction(this, true);
10877 handleResponse : function(response){
10878 if(this.form.reader){
10879 var rs = this.form.reader.read(response);
10880 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10882 success : rs.success,
10886 return Roo.decode(response.responseText);
10890 Roo.form.Action.ACTION_TYPES = {
10891 'load' : Roo.form.Action.Load,
10892 'submit' : Roo.form.Action.Submit
10901 * @class Roo.bootstrap.Form
10902 * @extends Roo.bootstrap.Component
10903 * Bootstrap Form class
10904 * @cfg {String} method GET | POST (default POST)
10905 * @cfg {String} labelAlign top | left (default top)
10906 * @cfg {String} align left | right - for navbars
10907 * @cfg {Boolean} loadMask load mask when submit (default true)
10911 * Create a new Form
10912 * @param {Object} config The config object
10916 Roo.bootstrap.Form = function(config){
10918 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10920 Roo.bootstrap.Form.popover.apply();
10924 * @event clientvalidation
10925 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10926 * @param {Form} this
10927 * @param {Boolean} valid true if the form has passed client-side validation
10929 clientvalidation: true,
10931 * @event beforeaction
10932 * Fires before any action is performed. Return false to cancel the action.
10933 * @param {Form} this
10934 * @param {Action} action The action to be performed
10936 beforeaction: true,
10938 * @event actionfailed
10939 * Fires when an action fails.
10940 * @param {Form} this
10941 * @param {Action} action The action that failed
10943 actionfailed : true,
10945 * @event actioncomplete
10946 * Fires when an action is completed.
10947 * @param {Form} this
10948 * @param {Action} action The action that completed
10950 actioncomplete : true
10954 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10957 * @cfg {String} method
10958 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10962 * @cfg {String} url
10963 * The URL to use for form actions if one isn't supplied in the action options.
10966 * @cfg {Boolean} fileUpload
10967 * Set to true if this form is a file upload.
10971 * @cfg {Object} baseParams
10972 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10976 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10980 * @cfg {Sting} align (left|right) for navbar forms
10985 activeAction : null,
10988 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10989 * element by passing it or its id or mask the form itself by passing in true.
10992 waitMsgTarget : false,
10997 * @cfg {Boolean} errorMask (true|false) default false
11002 * @cfg {Number} maskOffset Default 100
11007 * @cfg {Boolean} maskBody
11011 getAutoCreate : function(){
11015 method : this.method || 'POST',
11016 id : this.id || Roo.id(),
11019 if (this.parent().xtype.match(/^Nav/)) {
11020 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11024 if (this.labelAlign == 'left' ) {
11025 cfg.cls += ' form-horizontal';
11031 initEvents : function()
11033 this.el.on('submit', this.onSubmit, this);
11034 // this was added as random key presses on the form where triggering form submit.
11035 this.el.on('keypress', function(e) {
11036 if (e.getCharCode() != 13) {
11039 // we might need to allow it for textareas.. and some other items.
11040 // check e.getTarget().
11042 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11046 Roo.log("keypress blocked");
11048 e.preventDefault();
11054 onSubmit : function(e){
11059 * Returns true if client-side validation on the form is successful.
11062 isValid : function(){
11063 var items = this.getItems();
11065 var target = false;
11067 items.each(function(f){
11073 Roo.log('invalid field: ' + f.name);
11077 if(!target && f.el.isVisible(true)){
11083 if(this.errorMask && !valid){
11084 Roo.bootstrap.Form.popover.mask(this, target);
11091 * Returns true if any fields in this form have changed since their original load.
11094 isDirty : function(){
11096 var items = this.getItems();
11097 items.each(function(f){
11107 * Performs a predefined action (submit or load) or custom actions you define on this form.
11108 * @param {String} actionName The name of the action type
11109 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11110 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11111 * accept other config options):
11113 Property Type Description
11114 ---------------- --------------- ----------------------------------------------------------------------------------
11115 url String The url for the action (defaults to the form's url)
11116 method String The form method to use (defaults to the form's method, or POST if not defined)
11117 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11118 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11119 validate the form on the client (defaults to false)
11121 * @return {BasicForm} this
11123 doAction : function(action, options){
11124 if(typeof action == 'string'){
11125 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11127 if(this.fireEvent('beforeaction', this, action) !== false){
11128 this.beforeAction(action);
11129 action.run.defer(100, action);
11135 beforeAction : function(action){
11136 var o = action.options;
11141 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11143 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11146 // not really supported yet.. ??
11148 //if(this.waitMsgTarget === true){
11149 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11150 //}else if(this.waitMsgTarget){
11151 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11152 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11154 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11160 afterAction : function(action, success){
11161 this.activeAction = null;
11162 var o = action.options;
11167 Roo.get(document.body).unmask();
11173 //if(this.waitMsgTarget === true){
11174 // this.el.unmask();
11175 //}else if(this.waitMsgTarget){
11176 // this.waitMsgTarget.unmask();
11178 // Roo.MessageBox.updateProgress(1);
11179 // Roo.MessageBox.hide();
11186 Roo.callback(o.success, o.scope, [this, action]);
11187 this.fireEvent('actioncomplete', this, action);
11191 // failure condition..
11192 // we have a scenario where updates need confirming.
11193 // eg. if a locking scenario exists..
11194 // we look for { errors : { needs_confirm : true }} in the response.
11196 (typeof(action.result) != 'undefined') &&
11197 (typeof(action.result.errors) != 'undefined') &&
11198 (typeof(action.result.errors.needs_confirm) != 'undefined')
11201 Roo.log("not supported yet");
11204 Roo.MessageBox.confirm(
11205 "Change requires confirmation",
11206 action.result.errorMsg,
11211 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11221 Roo.callback(o.failure, o.scope, [this, action]);
11222 // show an error message if no failed handler is set..
11223 if (!this.hasListener('actionfailed')) {
11224 Roo.log("need to add dialog support");
11226 Roo.MessageBox.alert("Error",
11227 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11228 action.result.errorMsg :
11229 "Saving Failed, please check your entries or try again"
11234 this.fireEvent('actionfailed', this, action);
11239 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11240 * @param {String} id The value to search for
11243 findField : function(id){
11244 var items = this.getItems();
11245 var field = items.get(id);
11247 items.each(function(f){
11248 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11255 return field || null;
11258 * Mark fields in this form invalid in bulk.
11259 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11260 * @return {BasicForm} this
11262 markInvalid : function(errors){
11263 if(errors instanceof Array){
11264 for(var i = 0, len = errors.length; i < len; i++){
11265 var fieldError = errors[i];
11266 var f = this.findField(fieldError.id);
11268 f.markInvalid(fieldError.msg);
11274 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11275 field.markInvalid(errors[id]);
11279 //Roo.each(this.childForms || [], function (f) {
11280 // f.markInvalid(errors);
11287 * Set values for fields in this form in bulk.
11288 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11289 * @return {BasicForm} this
11291 setValues : function(values){
11292 if(values instanceof Array){ // array of objects
11293 for(var i = 0, len = values.length; i < len; i++){
11295 var f = this.findField(v.id);
11297 f.setValue(v.value);
11298 if(this.trackResetOnLoad){
11299 f.originalValue = f.getValue();
11303 }else{ // object hash
11306 if(typeof values[id] != 'function' && (field = this.findField(id))){
11308 if (field.setFromData &&
11309 field.valueField &&
11310 field.displayField &&
11311 // combos' with local stores can
11312 // be queried via setValue()
11313 // to set their value..
11314 (field.store && !field.store.isLocal)
11318 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11319 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11320 field.setFromData(sd);
11322 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11324 field.setFromData(values);
11327 field.setValue(values[id]);
11331 if(this.trackResetOnLoad){
11332 field.originalValue = field.getValue();
11338 //Roo.each(this.childForms || [], function (f) {
11339 // f.setValues(values);
11346 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11347 * they are returned as an array.
11348 * @param {Boolean} asString
11351 getValues : function(asString){
11352 //if (this.childForms) {
11353 // copy values from the child forms
11354 // Roo.each(this.childForms, function (f) {
11355 // this.setValues(f.getValues());
11361 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11362 if(asString === true){
11365 return Roo.urlDecode(fs);
11369 * Returns the fields in this form as an object with key/value pairs.
11370 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11373 getFieldValues : function(with_hidden)
11375 var items = this.getItems();
11377 items.each(function(f){
11379 if (!f.getName()) {
11383 var v = f.getValue();
11385 if (f.inputType =='radio') {
11386 if (typeof(ret[f.getName()]) == 'undefined') {
11387 ret[f.getName()] = ''; // empty..
11390 if (!f.el.dom.checked) {
11394 v = f.el.dom.value;
11398 if(f.xtype == 'MoneyField'){
11399 ret[f.currencyName] = f.getCurrency();
11402 // not sure if this supported any more..
11403 if ((typeof(v) == 'object') && f.getRawValue) {
11404 v = f.getRawValue() ; // dates..
11406 // combo boxes where name != hiddenName...
11407 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11408 ret[f.name] = f.getRawValue();
11410 ret[f.getName()] = v;
11417 * Clears all invalid messages in this form.
11418 * @return {BasicForm} this
11420 clearInvalid : function(){
11421 var items = this.getItems();
11423 items.each(function(f){
11431 * Resets this form.
11432 * @return {BasicForm} this
11434 reset : function(){
11435 var items = this.getItems();
11436 items.each(function(f){
11440 Roo.each(this.childForms || [], function (f) {
11448 getItems : function()
11450 var r=new Roo.util.MixedCollection(false, function(o){
11451 return o.id || (o.id = Roo.id());
11453 var iter = function(el) {
11460 Roo.each(el.items,function(e) {
11469 hideFields : function(items)
11471 Roo.each(items, function(i){
11473 var f = this.findField(i);
11484 showFields : function(items)
11486 Roo.each(items, function(i){
11488 var f = this.findField(i);
11501 Roo.apply(Roo.bootstrap.Form, {
11517 intervalID : false,
11523 if(this.isApplied){
11528 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11529 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11530 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11531 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11534 this.maskEl.top.enableDisplayMode("block");
11535 this.maskEl.left.enableDisplayMode("block");
11536 this.maskEl.bottom.enableDisplayMode("block");
11537 this.maskEl.right.enableDisplayMode("block");
11539 this.toolTip = new Roo.bootstrap.Tooltip({
11540 cls : 'roo-form-error-popover',
11542 'left' : ['r-l', [-2,0], 'right'],
11543 'right' : ['l-r', [2,0], 'left'],
11544 'bottom' : ['tl-bl', [0,2], 'top'],
11545 'top' : [ 'bl-tl', [0,-2], 'bottom']
11549 this.toolTip.render(Roo.get(document.body));
11551 this.toolTip.el.enableDisplayMode("block");
11553 Roo.get(document.body).on('click', function(){
11557 Roo.get(document.body).on('touchstart', function(){
11561 this.isApplied = true
11564 mask : function(form, target)
11568 this.target = target;
11570 if(!this.form.errorMask || !target.el){
11574 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11576 Roo.log(scrollable);
11578 var ot = this.target.el.calcOffsetsTo(scrollable);
11580 var scrollTo = ot[1] - this.form.maskOffset;
11582 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11584 scrollable.scrollTo('top', scrollTo);
11586 var box = this.target.el.getBox();
11588 var zIndex = Roo.bootstrap.Modal.zIndex++;
11591 this.maskEl.top.setStyle('position', 'absolute');
11592 this.maskEl.top.setStyle('z-index', zIndex);
11593 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11594 this.maskEl.top.setLeft(0);
11595 this.maskEl.top.setTop(0);
11596 this.maskEl.top.show();
11598 this.maskEl.left.setStyle('position', 'absolute');
11599 this.maskEl.left.setStyle('z-index', zIndex);
11600 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11601 this.maskEl.left.setLeft(0);
11602 this.maskEl.left.setTop(box.y - this.padding);
11603 this.maskEl.left.show();
11605 this.maskEl.bottom.setStyle('position', 'absolute');
11606 this.maskEl.bottom.setStyle('z-index', zIndex);
11607 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11608 this.maskEl.bottom.setLeft(0);
11609 this.maskEl.bottom.setTop(box.bottom + this.padding);
11610 this.maskEl.bottom.show();
11612 this.maskEl.right.setStyle('position', 'absolute');
11613 this.maskEl.right.setStyle('z-index', zIndex);
11614 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11615 this.maskEl.right.setLeft(box.right + this.padding);
11616 this.maskEl.right.setTop(box.y - this.padding);
11617 this.maskEl.right.show();
11619 this.toolTip.bindEl = this.target.el;
11621 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11623 var tip = this.target.blankText;
11625 if(this.target.getValue() !== '' ) {
11627 if (this.target.invalidText.length) {
11628 tip = this.target.invalidText;
11629 } else if (this.target.regexText.length){
11630 tip = this.target.regexText;
11634 this.toolTip.show(tip);
11636 this.intervalID = window.setInterval(function() {
11637 Roo.bootstrap.Form.popover.unmask();
11640 window.onwheel = function(){ return false;};
11642 (function(){ this.isMasked = true; }).defer(500, this);
11646 unmask : function()
11648 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11652 this.maskEl.top.setStyle('position', 'absolute');
11653 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11654 this.maskEl.top.hide();
11656 this.maskEl.left.setStyle('position', 'absolute');
11657 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11658 this.maskEl.left.hide();
11660 this.maskEl.bottom.setStyle('position', 'absolute');
11661 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11662 this.maskEl.bottom.hide();
11664 this.maskEl.right.setStyle('position', 'absolute');
11665 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11666 this.maskEl.right.hide();
11668 this.toolTip.hide();
11670 this.toolTip.el.hide();
11672 window.onwheel = function(){ return true;};
11674 if(this.intervalID){
11675 window.clearInterval(this.intervalID);
11676 this.intervalID = false;
11679 this.isMasked = false;
11689 * Ext JS Library 1.1.1
11690 * Copyright(c) 2006-2007, Ext JS, LLC.
11692 * Originally Released Under LGPL - original licence link has changed is not relivant.
11695 * <script type="text/javascript">
11698 * @class Roo.form.VTypes
11699 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11702 Roo.form.VTypes = function(){
11703 // closure these in so they are only created once.
11704 var alpha = /^[a-zA-Z_]+$/;
11705 var alphanum = /^[a-zA-Z0-9_]+$/;
11706 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11707 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11709 // All these messages and functions are configurable
11712 * The function used to validate email addresses
11713 * @param {String} value The email address
11715 'email' : function(v){
11716 return email.test(v);
11719 * The error text to display when the email validation function returns false
11722 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11724 * The keystroke filter mask to be applied on email input
11727 'emailMask' : /[a-z0-9_\.\-@]/i,
11730 * The function used to validate URLs
11731 * @param {String} value The URL
11733 'url' : function(v){
11734 return url.test(v);
11737 * The error text to display when the url validation function returns false
11740 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11743 * The function used to validate alpha values
11744 * @param {String} value The value
11746 'alpha' : function(v){
11747 return alpha.test(v);
11750 * The error text to display when the alpha validation function returns false
11753 'alphaText' : 'This field should only contain letters and _',
11755 * The keystroke filter mask to be applied on alpha input
11758 'alphaMask' : /[a-z_]/i,
11761 * The function used to validate alphanumeric values
11762 * @param {String} value The value
11764 'alphanum' : function(v){
11765 return alphanum.test(v);
11768 * The error text to display when the alphanumeric validation function returns false
11771 'alphanumText' : 'This field should only contain letters, numbers and _',
11773 * The keystroke filter mask to be applied on alphanumeric input
11776 'alphanumMask' : /[a-z0-9_]/i
11786 * @class Roo.bootstrap.Input
11787 * @extends Roo.bootstrap.Component
11788 * Bootstrap Input class
11789 * @cfg {Boolean} disabled is it disabled
11790 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11791 * @cfg {String} name name of the input
11792 * @cfg {string} fieldLabel - the label associated
11793 * @cfg {string} placeholder - placeholder to put in text.
11794 * @cfg {string} before - input group add on before
11795 * @cfg {string} after - input group add on after
11796 * @cfg {string} size - (lg|sm) or leave empty..
11797 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11798 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11799 * @cfg {Number} md colspan out of 12 for computer-sized screens
11800 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11801 * @cfg {string} value default value of the input
11802 * @cfg {Number} labelWidth set the width of label
11803 * @cfg {Number} labellg set the width of label (1-12)
11804 * @cfg {Number} labelmd set the width of label (1-12)
11805 * @cfg {Number} labelsm set the width of label (1-12)
11806 * @cfg {Number} labelxs set the width of label (1-12)
11807 * @cfg {String} labelAlign (top|left)
11808 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11809 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11810 * @cfg {String} indicatorpos (left|right) default left
11811 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11812 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11813 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11815 * @cfg {String} align (left|center|right) Default left
11816 * @cfg {Boolean} forceFeedback (true|false) Default false
11819 * Create a new Input
11820 * @param {Object} config The config object
11823 Roo.bootstrap.Input = function(config){
11825 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11830 * Fires when this field receives input focus.
11831 * @param {Roo.form.Field} this
11836 * Fires when this field loses input focus.
11837 * @param {Roo.form.Field} this
11841 * @event specialkey
11842 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11843 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11844 * @param {Roo.form.Field} this
11845 * @param {Roo.EventObject} e The event object
11850 * Fires just before the field blurs if the field value has changed.
11851 * @param {Roo.form.Field} this
11852 * @param {Mixed} newValue The new value
11853 * @param {Mixed} oldValue The original value
11858 * Fires after the field has been marked as invalid.
11859 * @param {Roo.form.Field} this
11860 * @param {String} msg The validation message
11865 * Fires after the field has been validated with no errors.
11866 * @param {Roo.form.Field} this
11871 * Fires after the key up
11872 * @param {Roo.form.Field} this
11873 * @param {Roo.EventObject} e The event Object
11878 * Fires after the user pastes into input
11879 * @param {Roo.form.Field} this
11880 * @param {Roo.EventObject} e The event Object
11886 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11888 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11889 automatic validation (defaults to "keyup").
11891 validationEvent : "keyup",
11893 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11895 validateOnBlur : true,
11897 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11899 validationDelay : 250,
11901 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11903 focusClass : "x-form-focus", // not needed???
11907 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11909 invalidClass : "has-warning",
11912 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11914 validClass : "has-success",
11917 * @cfg {Boolean} hasFeedback (true|false) default true
11919 hasFeedback : true,
11922 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11924 invalidFeedbackClass : "glyphicon-warning-sign",
11927 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11929 validFeedbackClass : "glyphicon-ok",
11932 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11934 selectOnFocus : false,
11937 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11941 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11946 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11948 disableKeyFilter : false,
11951 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11955 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11959 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11961 blankText : "Please complete this mandatory field",
11964 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11968 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11970 maxLength : Number.MAX_VALUE,
11972 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11974 minLengthText : "The minimum length for this field is {0}",
11976 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11978 maxLengthText : "The maximum length for this field is {0}",
11982 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11983 * If available, this function will be called only after the basic validators all return true, and will be passed the
11984 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11988 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11989 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11990 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11994 * @cfg {String} regexText -- Depricated - use Invalid Text
11999 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12005 autocomplete: false,
12009 inputType : 'text',
12012 placeholder: false,
12017 preventMark: false,
12018 isFormField : true,
12021 labelAlign : false,
12024 formatedValue : false,
12025 forceFeedback : false,
12027 indicatorpos : 'left',
12037 parentLabelAlign : function()
12040 while (parent.parent()) {
12041 parent = parent.parent();
12042 if (typeof(parent.labelAlign) !='undefined') {
12043 return parent.labelAlign;
12050 getAutoCreate : function()
12052 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12058 if(this.inputType != 'hidden'){
12059 cfg.cls = 'form-group' //input-group
12065 type : this.inputType,
12066 value : this.value,
12067 cls : 'form-control',
12068 placeholder : this.placeholder || '',
12069 autocomplete : this.autocomplete || 'new-password'
12071 if (this.inputType == 'file') {
12072 input.style = 'overflow:hidden'; // why not in CSS?
12075 if(this.capture.length){
12076 input.capture = this.capture;
12079 if(this.accept.length){
12080 input.accept = this.accept + "/*";
12084 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12087 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12088 input.maxLength = this.maxLength;
12091 if (this.disabled) {
12092 input.disabled=true;
12095 if (this.readOnly) {
12096 input.readonly=true;
12100 input.name = this.name;
12104 input.cls += ' input-' + this.size;
12108 ['xs','sm','md','lg'].map(function(size){
12109 if (settings[size]) {
12110 cfg.cls += ' col-' + size + '-' + settings[size];
12114 var inputblock = input;
12118 cls: 'glyphicon form-control-feedback'
12121 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12124 cls : 'has-feedback',
12132 if (this.before || this.after) {
12135 cls : 'input-group',
12139 if (this.before && typeof(this.before) == 'string') {
12141 inputblock.cn.push({
12143 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12147 if (this.before && typeof(this.before) == 'object') {
12148 this.before = Roo.factory(this.before);
12150 inputblock.cn.push({
12152 cls : 'roo-input-before input-group-prepend input-group-' +
12153 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12157 inputblock.cn.push(input);
12159 if (this.after && typeof(this.after) == 'string') {
12160 inputblock.cn.push({
12162 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12166 if (this.after && typeof(this.after) == 'object') {
12167 this.after = Roo.factory(this.after);
12169 inputblock.cn.push({
12171 cls : 'roo-input-after input-group-append input-group-' +
12172 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12176 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12177 inputblock.cls += ' has-feedback';
12178 inputblock.cn.push(feedback);
12183 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12184 tooltip : 'This field is required'
12186 if (this.allowBlank ) {
12187 indicator.style = this.allowBlank ? ' display:none' : '';
12189 if (align ==='left' && this.fieldLabel.length) {
12191 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12198 cls : 'control-label col-form-label',
12199 html : this.fieldLabel
12210 var labelCfg = cfg.cn[1];
12211 var contentCfg = cfg.cn[2];
12213 if(this.indicatorpos == 'right'){
12218 cls : 'control-label col-form-label',
12222 html : this.fieldLabel
12236 labelCfg = cfg.cn[0];
12237 contentCfg = cfg.cn[1];
12241 if(this.labelWidth > 12){
12242 labelCfg.style = "width: " + this.labelWidth + 'px';
12245 if(this.labelWidth < 13 && this.labelmd == 0){
12246 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12249 if(this.labellg > 0){
12250 labelCfg.cls += ' col-lg-' + this.labellg;
12251 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12254 if(this.labelmd > 0){
12255 labelCfg.cls += ' col-md-' + this.labelmd;
12256 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12259 if(this.labelsm > 0){
12260 labelCfg.cls += ' col-sm-' + this.labelsm;
12261 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12264 if(this.labelxs > 0){
12265 labelCfg.cls += ' col-xs-' + this.labelxs;
12266 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12270 } else if ( this.fieldLabel.length) {
12277 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12278 tooltip : 'This field is required',
12279 style : this.allowBlank ? ' display:none' : ''
12283 //cls : 'input-group-addon',
12284 html : this.fieldLabel
12292 if(this.indicatorpos == 'right'){
12297 //cls : 'input-group-addon',
12298 html : this.fieldLabel
12303 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12304 tooltip : 'This field is required',
12305 style : this.allowBlank ? ' display:none' : ''
12325 if (this.parentType === 'Navbar' && this.parent().bar) {
12326 cfg.cls += ' navbar-form';
12329 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12330 // on BS4 we do this only if not form
12331 cfg.cls += ' navbar-form';
12339 * return the real input element.
12341 inputEl: function ()
12343 return this.el.select('input.form-control',true).first();
12346 tooltipEl : function()
12348 return this.inputEl();
12351 indicatorEl : function()
12353 if (Roo.bootstrap.version == 4) {
12354 return false; // not enabled in v4 yet.
12357 var indicator = this.el.select('i.roo-required-indicator',true).first();
12367 setDisabled : function(v)
12369 var i = this.inputEl().dom;
12371 i.removeAttribute('disabled');
12375 i.setAttribute('disabled','true');
12377 initEvents : function()
12380 this.inputEl().on("keydown" , this.fireKey, this);
12381 this.inputEl().on("focus", this.onFocus, this);
12382 this.inputEl().on("blur", this.onBlur, this);
12384 this.inputEl().relayEvent('keyup', this);
12385 this.inputEl().relayEvent('paste', this);
12387 this.indicator = this.indicatorEl();
12389 if(this.indicator){
12390 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12393 // reference to original value for reset
12394 this.originalValue = this.getValue();
12395 //Roo.form.TextField.superclass.initEvents.call(this);
12396 if(this.validationEvent == 'keyup'){
12397 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12398 this.inputEl().on('keyup', this.filterValidation, this);
12400 else if(this.validationEvent !== false){
12401 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12404 if(this.selectOnFocus){
12405 this.on("focus", this.preFocus, this);
12408 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12409 this.inputEl().on("keypress", this.filterKeys, this);
12411 this.inputEl().relayEvent('keypress', this);
12414 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12415 this.el.on("click", this.autoSize, this);
12418 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12419 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12422 if (typeof(this.before) == 'object') {
12423 this.before.render(this.el.select('.roo-input-before',true).first());
12425 if (typeof(this.after) == 'object') {
12426 this.after.render(this.el.select('.roo-input-after',true).first());
12429 this.inputEl().on('change', this.onChange, this);
12432 filterValidation : function(e){
12433 if(!e.isNavKeyPress()){
12434 this.validationTask.delay(this.validationDelay);
12438 * Validates the field value
12439 * @return {Boolean} True if the value is valid, else false
12441 validate : function(){
12442 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12443 if(this.disabled || this.validateValue(this.getRawValue())){
12448 this.markInvalid();
12454 * Validates a value according to the field's validation rules and marks the field as invalid
12455 * if the validation fails
12456 * @param {Mixed} value The value to validate
12457 * @return {Boolean} True if the value is valid, else false
12459 validateValue : function(value)
12461 if(this.getVisibilityEl().hasClass('hidden')){
12465 if(value.length < 1) { // if it's blank
12466 if(this.allowBlank){
12472 if(value.length < this.minLength){
12475 if(value.length > this.maxLength){
12479 var vt = Roo.form.VTypes;
12480 if(!vt[this.vtype](value, this)){
12484 if(typeof this.validator == "function"){
12485 var msg = this.validator(value);
12489 if (typeof(msg) == 'string') {
12490 this.invalidText = msg;
12494 if(this.regex && !this.regex.test(value)){
12502 fireKey : function(e){
12503 //Roo.log('field ' + e.getKey());
12504 if(e.isNavKeyPress()){
12505 this.fireEvent("specialkey", this, e);
12508 focus : function (selectText){
12510 this.inputEl().focus();
12511 if(selectText === true){
12512 this.inputEl().dom.select();
12518 onFocus : function(){
12519 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12520 // this.el.addClass(this.focusClass);
12522 if(!this.hasFocus){
12523 this.hasFocus = true;
12524 this.startValue = this.getValue();
12525 this.fireEvent("focus", this);
12529 beforeBlur : Roo.emptyFn,
12533 onBlur : function(){
12535 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12536 //this.el.removeClass(this.focusClass);
12538 this.hasFocus = false;
12539 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12542 var v = this.getValue();
12543 if(String(v) !== String(this.startValue)){
12544 this.fireEvent('change', this, v, this.startValue);
12546 this.fireEvent("blur", this);
12549 onChange : function(e)
12551 var v = this.getValue();
12552 if(String(v) !== String(this.startValue)){
12553 this.fireEvent('change', this, v, this.startValue);
12559 * Resets the current field value to the originally loaded value and clears any validation messages
12561 reset : function(){
12562 this.setValue(this.originalValue);
12566 * Returns the name of the field
12567 * @return {Mixed} name The name field
12569 getName: function(){
12573 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12574 * @return {Mixed} value The field value
12576 getValue : function(){
12578 var v = this.inputEl().getValue();
12583 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12584 * @return {Mixed} value The field value
12586 getRawValue : function(){
12587 var v = this.inputEl().getValue();
12593 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12594 * @param {Mixed} value The value to set
12596 setRawValue : function(v){
12597 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12600 selectText : function(start, end){
12601 var v = this.getRawValue();
12603 start = start === undefined ? 0 : start;
12604 end = end === undefined ? v.length : end;
12605 var d = this.inputEl().dom;
12606 if(d.setSelectionRange){
12607 d.setSelectionRange(start, end);
12608 }else if(d.createTextRange){
12609 var range = d.createTextRange();
12610 range.moveStart("character", start);
12611 range.moveEnd("character", v.length-end);
12618 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12619 * @param {Mixed} value The value to set
12621 setValue : function(v){
12624 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12630 processValue : function(value){
12631 if(this.stripCharsRe){
12632 var newValue = value.replace(this.stripCharsRe, '');
12633 if(newValue !== value){
12634 this.setRawValue(newValue);
12641 preFocus : function(){
12643 if(this.selectOnFocus){
12644 this.inputEl().dom.select();
12647 filterKeys : function(e){
12648 var k = e.getKey();
12649 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12652 var c = e.getCharCode(), cc = String.fromCharCode(c);
12653 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12656 if(!this.maskRe.test(cc)){
12661 * Clear any invalid styles/messages for this field
12663 clearInvalid : function(){
12665 if(!this.el || this.preventMark){ // not rendered
12670 this.el.removeClass([this.invalidClass, 'is-invalid']);
12672 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12674 var feedback = this.el.select('.form-control-feedback', true).first();
12677 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12682 if(this.indicator){
12683 this.indicator.removeClass('visible');
12684 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12687 this.fireEvent('valid', this);
12691 * Mark this field as valid
12693 markValid : function()
12695 if(!this.el || this.preventMark){ // not rendered...
12699 this.el.removeClass([this.invalidClass, this.validClass]);
12700 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12702 var feedback = this.el.select('.form-control-feedback', true).first();
12705 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12708 if(this.indicator){
12709 this.indicator.removeClass('visible');
12710 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12718 if(this.allowBlank && !this.getRawValue().length){
12721 if (Roo.bootstrap.version == 3) {
12722 this.el.addClass(this.validClass);
12724 this.inputEl().addClass('is-valid');
12727 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12729 var feedback = this.el.select('.form-control-feedback', true).first();
12732 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12733 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12738 this.fireEvent('valid', this);
12742 * Mark this field as invalid
12743 * @param {String} msg The validation message
12745 markInvalid : function(msg)
12747 if(!this.el || this.preventMark){ // not rendered
12751 this.el.removeClass([this.invalidClass, this.validClass]);
12752 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12754 var feedback = this.el.select('.form-control-feedback', true).first();
12757 this.el.select('.form-control-feedback', true).first().removeClass(
12758 [this.invalidFeedbackClass, this.validFeedbackClass]);
12765 if(this.allowBlank && !this.getRawValue().length){
12769 if(this.indicator){
12770 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12771 this.indicator.addClass('visible');
12773 if (Roo.bootstrap.version == 3) {
12774 this.el.addClass(this.invalidClass);
12776 this.inputEl().addClass('is-invalid');
12781 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12783 var feedback = this.el.select('.form-control-feedback', true).first();
12786 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12788 if(this.getValue().length || this.forceFeedback){
12789 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12796 this.fireEvent('invalid', this, msg);
12799 SafariOnKeyDown : function(event)
12801 // this is a workaround for a password hang bug on chrome/ webkit.
12802 if (this.inputEl().dom.type != 'password') {
12806 var isSelectAll = false;
12808 if(this.inputEl().dom.selectionEnd > 0){
12809 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12811 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12812 event.preventDefault();
12817 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12819 event.preventDefault();
12820 // this is very hacky as keydown always get's upper case.
12822 var cc = String.fromCharCode(event.getCharCode());
12823 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12827 adjustWidth : function(tag, w){
12828 tag = tag.toLowerCase();
12829 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12830 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12831 if(tag == 'input'){
12834 if(tag == 'textarea'){
12837 }else if(Roo.isOpera){
12838 if(tag == 'input'){
12841 if(tag == 'textarea'){
12849 setFieldLabel : function(v)
12851 if(!this.rendered){
12855 if(this.indicatorEl()){
12856 var ar = this.el.select('label > span',true);
12858 if (ar.elements.length) {
12859 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12860 this.fieldLabel = v;
12864 var br = this.el.select('label',true);
12866 if(br.elements.length) {
12867 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12868 this.fieldLabel = v;
12872 Roo.log('Cannot Found any of label > span || label in input');
12876 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12877 this.fieldLabel = v;
12892 * @class Roo.bootstrap.TextArea
12893 * @extends Roo.bootstrap.Input
12894 * Bootstrap TextArea class
12895 * @cfg {Number} cols Specifies the visible width of a text area
12896 * @cfg {Number} rows Specifies the visible number of lines in a text area
12897 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12898 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12899 * @cfg {string} html text
12902 * Create a new TextArea
12903 * @param {Object} config The config object
12906 Roo.bootstrap.TextArea = function(config){
12907 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12911 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12921 getAutoCreate : function(){
12923 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12929 if(this.inputType != 'hidden'){
12930 cfg.cls = 'form-group' //input-group
12938 value : this.value || '',
12939 html: this.html || '',
12940 cls : 'form-control',
12941 placeholder : this.placeholder || ''
12945 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12946 input.maxLength = this.maxLength;
12950 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12954 input.cols = this.cols;
12957 if (this.readOnly) {
12958 input.readonly = true;
12962 input.name = this.name;
12966 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12970 ['xs','sm','md','lg'].map(function(size){
12971 if (settings[size]) {
12972 cfg.cls += ' col-' + size + '-' + settings[size];
12976 var inputblock = input;
12978 if(this.hasFeedback && !this.allowBlank){
12982 cls: 'glyphicon form-control-feedback'
12986 cls : 'has-feedback',
12995 if (this.before || this.after) {
12998 cls : 'input-group',
13002 inputblock.cn.push({
13004 cls : 'input-group-addon',
13009 inputblock.cn.push(input);
13011 if(this.hasFeedback && !this.allowBlank){
13012 inputblock.cls += ' has-feedback';
13013 inputblock.cn.push(feedback);
13017 inputblock.cn.push({
13019 cls : 'input-group-addon',
13026 if (align ==='left' && this.fieldLabel.length) {
13031 cls : 'control-label',
13032 html : this.fieldLabel
13043 if(this.labelWidth > 12){
13044 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13047 if(this.labelWidth < 13 && this.labelmd == 0){
13048 this.labelmd = this.labelWidth;
13051 if(this.labellg > 0){
13052 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13053 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13056 if(this.labelmd > 0){
13057 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13058 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13061 if(this.labelsm > 0){
13062 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13063 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13066 if(this.labelxs > 0){
13067 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13068 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13071 } else if ( this.fieldLabel.length) {
13076 //cls : 'input-group-addon',
13077 html : this.fieldLabel
13095 if (this.disabled) {
13096 input.disabled=true;
13103 * return the real textarea element.
13105 inputEl: function ()
13107 return this.el.select('textarea.form-control',true).first();
13111 * Clear any invalid styles/messages for this field
13113 clearInvalid : function()
13116 if(!this.el || this.preventMark){ // not rendered
13120 var label = this.el.select('label', true).first();
13121 var icon = this.el.select('i.fa-star', true).first();
13126 this.el.removeClass( this.validClass);
13127 this.inputEl().removeClass('is-invalid');
13129 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13131 var feedback = this.el.select('.form-control-feedback', true).first();
13134 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13139 this.fireEvent('valid', this);
13143 * Mark this field as valid
13145 markValid : function()
13147 if(!this.el || this.preventMark){ // not rendered
13151 this.el.removeClass([this.invalidClass, this.validClass]);
13152 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13154 var feedback = this.el.select('.form-control-feedback', true).first();
13157 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13160 if(this.disabled || this.allowBlank){
13164 var label = this.el.select('label', true).first();
13165 var icon = this.el.select('i.fa-star', true).first();
13170 if (Roo.bootstrap.version == 3) {
13171 this.el.addClass(this.validClass);
13173 this.inputEl().addClass('is-valid');
13177 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13179 var feedback = this.el.select('.form-control-feedback', true).first();
13182 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13183 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13188 this.fireEvent('valid', this);
13192 * Mark this field as invalid
13193 * @param {String} msg The validation message
13195 markInvalid : function(msg)
13197 if(!this.el || this.preventMark){ // not rendered
13201 this.el.removeClass([this.invalidClass, this.validClass]);
13202 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13204 var feedback = this.el.select('.form-control-feedback', true).first();
13207 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13210 if(this.disabled || this.allowBlank){
13214 var label = this.el.select('label', true).first();
13215 var icon = this.el.select('i.fa-star', true).first();
13217 if(!this.getValue().length && label && !icon){
13218 this.el.createChild({
13220 cls : 'text-danger fa fa-lg fa-star',
13221 tooltip : 'This field is required',
13222 style : 'margin-right:5px;'
13226 if (Roo.bootstrap.version == 3) {
13227 this.el.addClass(this.invalidClass);
13229 this.inputEl().addClass('is-invalid');
13232 // fixme ... this may be depricated need to test..
13233 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13235 var feedback = this.el.select('.form-control-feedback', true).first();
13238 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13240 if(this.getValue().length || this.forceFeedback){
13241 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13248 this.fireEvent('invalid', this, msg);
13256 * trigger field - base class for combo..
13261 * @class Roo.bootstrap.TriggerField
13262 * @extends Roo.bootstrap.Input
13263 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13264 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13265 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13266 * for which you can provide a custom implementation. For example:
13268 var trigger = new Roo.bootstrap.TriggerField();
13269 trigger.onTriggerClick = myTriggerFn;
13270 trigger.applyTo('my-field');
13273 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13274 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13275 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13276 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13277 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13280 * Create a new TriggerField.
13281 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13282 * to the base TextField)
13284 Roo.bootstrap.TriggerField = function(config){
13285 this.mimicing = false;
13286 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13289 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13291 * @cfg {String} triggerClass A CSS class to apply to the trigger
13294 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13299 * @cfg {Boolean} removable (true|false) special filter default false
13303 /** @cfg {Boolean} grow @hide */
13304 /** @cfg {Number} growMin @hide */
13305 /** @cfg {Number} growMax @hide */
13311 autoSize: Roo.emptyFn,
13315 deferHeight : true,
13318 actionMode : 'wrap',
13323 getAutoCreate : function(){
13325 var align = this.labelAlign || this.parentLabelAlign();
13330 cls: 'form-group' //input-group
13337 type : this.inputType,
13338 cls : 'form-control',
13339 autocomplete: 'new-password',
13340 placeholder : this.placeholder || ''
13344 input.name = this.name;
13347 input.cls += ' input-' + this.size;
13350 if (this.disabled) {
13351 input.disabled=true;
13354 var inputblock = input;
13356 if(this.hasFeedback && !this.allowBlank){
13360 cls: 'glyphicon form-control-feedback'
13363 if(this.removable && !this.editable ){
13365 cls : 'has-feedback',
13371 cls : 'roo-combo-removable-btn close'
13378 cls : 'has-feedback',
13387 if(this.removable && !this.editable ){
13389 cls : 'roo-removable',
13395 cls : 'roo-combo-removable-btn close'
13402 if (this.before || this.after) {
13405 cls : 'input-group',
13409 inputblock.cn.push({
13411 cls : 'input-group-addon input-group-prepend input-group-text',
13416 inputblock.cn.push(input);
13418 if(this.hasFeedback && !this.allowBlank){
13419 inputblock.cls += ' has-feedback';
13420 inputblock.cn.push(feedback);
13424 inputblock.cn.push({
13426 cls : 'input-group-addon input-group-append input-group-text',
13435 var ibwrap = inputblock;
13440 cls: 'roo-select2-choices',
13444 cls: 'roo-select2-search-field',
13456 cls: 'roo-select2-container input-group',
13461 cls: 'form-hidden-field'
13467 if(!this.multiple && this.showToggleBtn){
13473 if (this.caret != false) {
13476 cls: 'fa fa-' + this.caret
13483 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13485 Roo.bootstrap.version == 3 ? caret : '',
13488 cls: 'combobox-clear',
13502 combobox.cls += ' roo-select2-container-multi';
13506 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13507 tooltip : 'This field is required'
13509 if (Roo.bootstrap.version == 4) {
13512 style : 'display:none'
13517 if (align ==='left' && this.fieldLabel.length) {
13519 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13526 cls : 'control-label',
13527 html : this.fieldLabel
13539 var labelCfg = cfg.cn[1];
13540 var contentCfg = cfg.cn[2];
13542 if(this.indicatorpos == 'right'){
13547 cls : 'control-label',
13551 html : this.fieldLabel
13565 labelCfg = cfg.cn[0];
13566 contentCfg = cfg.cn[1];
13569 if(this.labelWidth > 12){
13570 labelCfg.style = "width: " + this.labelWidth + 'px';
13573 if(this.labelWidth < 13 && this.labelmd == 0){
13574 this.labelmd = this.labelWidth;
13577 if(this.labellg > 0){
13578 labelCfg.cls += ' col-lg-' + this.labellg;
13579 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13582 if(this.labelmd > 0){
13583 labelCfg.cls += ' col-md-' + this.labelmd;
13584 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13587 if(this.labelsm > 0){
13588 labelCfg.cls += ' col-sm-' + this.labelsm;
13589 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13592 if(this.labelxs > 0){
13593 labelCfg.cls += ' col-xs-' + this.labelxs;
13594 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13597 } else if ( this.fieldLabel.length) {
13598 // Roo.log(" label");
13603 //cls : 'input-group-addon',
13604 html : this.fieldLabel
13612 if(this.indicatorpos == 'right'){
13620 html : this.fieldLabel
13634 // Roo.log(" no label && no align");
13641 ['xs','sm','md','lg'].map(function(size){
13642 if (settings[size]) {
13643 cfg.cls += ' col-' + size + '-' + settings[size];
13654 onResize : function(w, h){
13655 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13656 // if(typeof w == 'number'){
13657 // var x = w - this.trigger.getWidth();
13658 // this.inputEl().setWidth(this.adjustWidth('input', x));
13659 // this.trigger.setStyle('left', x+'px');
13664 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13667 getResizeEl : function(){
13668 return this.inputEl();
13672 getPositionEl : function(){
13673 return this.inputEl();
13677 alignErrorIcon : function(){
13678 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13682 initEvents : function(){
13686 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13687 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13688 if(!this.multiple && this.showToggleBtn){
13689 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13690 if(this.hideTrigger){
13691 this.trigger.setDisplayed(false);
13693 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13697 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13700 if(this.removable && !this.editable && !this.tickable){
13701 var close = this.closeTriggerEl();
13704 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13705 close.on('click', this.removeBtnClick, this, close);
13709 //this.trigger.addClassOnOver('x-form-trigger-over');
13710 //this.trigger.addClassOnClick('x-form-trigger-click');
13713 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13717 closeTriggerEl : function()
13719 var close = this.el.select('.roo-combo-removable-btn', true).first();
13720 return close ? close : false;
13723 removeBtnClick : function(e, h, el)
13725 e.preventDefault();
13727 if(this.fireEvent("remove", this) !== false){
13729 this.fireEvent("afterremove", this)
13733 createList : function()
13735 this.list = Roo.get(document.body).createChild({
13736 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13737 cls: 'typeahead typeahead-long dropdown-menu shadow',
13738 style: 'display:none'
13741 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13746 initTrigger : function(){
13751 onDestroy : function(){
13753 this.trigger.removeAllListeners();
13754 // this.trigger.remove();
13757 // this.wrap.remove();
13759 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13763 onFocus : function(){
13764 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13766 if(!this.mimicing){
13767 this.wrap.addClass('x-trigger-wrap-focus');
13768 this.mimicing = true;
13769 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13770 if(this.monitorTab){
13771 this.el.on("keydown", this.checkTab, this);
13778 checkTab : function(e){
13779 if(e.getKey() == e.TAB){
13780 this.triggerBlur();
13785 onBlur : function(){
13790 mimicBlur : function(e, t){
13792 if(!this.wrap.contains(t) && this.validateBlur()){
13793 this.triggerBlur();
13799 triggerBlur : function(){
13800 this.mimicing = false;
13801 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13802 if(this.monitorTab){
13803 this.el.un("keydown", this.checkTab, this);
13805 //this.wrap.removeClass('x-trigger-wrap-focus');
13806 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13810 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13811 validateBlur : function(e, t){
13816 onDisable : function(){
13817 this.inputEl().dom.disabled = true;
13818 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13820 // this.wrap.addClass('x-item-disabled');
13825 onEnable : function(){
13826 this.inputEl().dom.disabled = false;
13827 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13829 // this.el.removeClass('x-item-disabled');
13834 onShow : function(){
13835 var ae = this.getActionEl();
13838 ae.dom.style.display = '';
13839 ae.dom.style.visibility = 'visible';
13845 onHide : function(){
13846 var ae = this.getActionEl();
13847 ae.dom.style.display = 'none';
13851 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13852 * by an implementing function.
13854 * @param {EventObject} e
13856 onTriggerClick : Roo.emptyFn
13864 * @class Roo.bootstrap.CardUploader
13865 * @extends Roo.bootstrap.Button
13866 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13867 * @cfg {Number} errorTimeout default 3000
13868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13869 * @cfg {Array} html The button text.
13873 * Create a new CardUploader
13874 * @param {Object} config The config object
13877 Roo.bootstrap.CardUploader = function(config){
13881 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13884 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13892 * When a image is clicked on - and needs to display a slideshow or similar..
13893 * @param {Roo.bootstrap.Card} this
13894 * @param {Object} The image information data
13900 * When a the download link is clicked
13901 * @param {Roo.bootstrap.Card} this
13902 * @param {Object} The image information data contains
13909 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13912 errorTimeout : 3000,
13916 fileCollection : false,
13919 getAutoCreate : function()
13923 cls :'form-group' ,
13928 //cls : 'input-group-addon',
13929 html : this.fieldLabel
13937 value : this.value,
13938 cls : 'd-none form-control'
13943 multiple : 'multiple',
13945 cls : 'd-none roo-card-upload-selector'
13949 cls : 'roo-card-uploader-button-container w-100 mb-2'
13952 cls : 'card-columns roo-card-uploader-container'
13962 getChildContainer : function() /// what children are added to.
13964 return this.containerEl;
13967 getButtonContainer : function() /// what children are added to.
13969 return this.el.select(".roo-card-uploader-button-container").first();
13972 initEvents : function()
13975 Roo.bootstrap.Input.prototype.initEvents.call(this);
13979 xns: Roo.bootstrap,
13982 container_method : 'getButtonContainer' ,
13983 html : this.html, // fix changable?
13986 'click' : function(btn, e) {
13995 this.urlAPI = (window.createObjectURL && window) ||
13996 (window.URL && URL.revokeObjectURL && URL) ||
13997 (window.webkitURL && webkitURL);
14002 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14004 this.selectorEl.on('change', this.onFileSelected, this);
14007 this.images.forEach(function(img) {
14010 this.images = false;
14012 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14018 onClick : function(e)
14020 e.preventDefault();
14022 this.selectorEl.dom.click();
14026 onFileSelected : function(e)
14028 e.preventDefault();
14030 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14034 Roo.each(this.selectorEl.dom.files, function(file){
14035 this.addFile(file);
14044 addFile : function(file)
14047 if(typeof(file) === 'string'){
14048 throw "Add file by name?"; // should not happen
14052 if(!file || !this.urlAPI){
14062 var url = _this.urlAPI.createObjectURL( file);
14065 id : Roo.bootstrap.CardUploader.ID--,
14066 is_uploaded : false,
14070 mimetype : file.type,
14078 * addCard - add an Attachment to the uploader
14079 * @param data - the data about the image to upload
14083 title : "Title of file",
14084 is_uploaded : false,
14085 src : "http://.....",
14086 srcfile : { the File upload object },
14087 mimetype : file.type,
14090 .. any other data...
14096 addCard : function (data)
14098 // hidden input element?
14099 // if the file is not an image...
14100 //then we need to use something other that and header_image
14105 xns : Roo.bootstrap,
14106 xtype : 'CardFooter',
14109 xns : Roo.bootstrap,
14115 xns : Roo.bootstrap,
14117 html : String.format("<small>{0}</small>", data.title),
14118 cls : 'col-10 text-left',
14123 click : function() {
14125 t.fireEvent( "download", t, data );
14131 xns : Roo.bootstrap,
14133 style: 'max-height: 28px; ',
14139 click : function() {
14140 t.removeCard(data.id)
14152 var cn = this.addxtype(
14155 xns : Roo.bootstrap,
14158 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14159 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14160 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14165 initEvents : function() {
14166 Roo.bootstrap.Card.prototype.initEvents.call(this);
14168 this.imgEl = this.el.select('.card-img-top').first();
14170 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14171 this.imgEl.set({ 'pointer' : 'cursor' });
14174 this.getCardFooter().addClass('p-1');
14181 // dont' really need ot update items.
14182 // this.items.push(cn);
14183 this.fileCollection.add(cn);
14185 if (!data.srcfile) {
14186 this.updateInput();
14191 var reader = new FileReader();
14192 reader.addEventListener("load", function() {
14193 data.srcdata = reader.result;
14196 reader.readAsDataURL(data.srcfile);
14201 removeCard : function(id)
14204 var card = this.fileCollection.get(id);
14205 card.data.is_deleted = 1;
14206 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14207 //this.fileCollection.remove(card);
14208 //this.items = this.items.filter(function(e) { return e != card });
14209 // dont' really need ot update items.
14210 card.el.dom.parentNode.removeChild(card.el.dom);
14211 this.updateInput();
14217 this.fileCollection.each(function(card) {
14218 if (card.el.dom && card.el.dom.parentNode) {
14219 card.el.dom.parentNode.removeChild(card.el.dom);
14222 this.fileCollection.clear();
14223 this.updateInput();
14226 updateInput : function()
14229 this.fileCollection.each(function(e) {
14233 this.inputEl().dom.value = JSON.stringify(data);
14243 Roo.bootstrap.CardUploader.ID = -1;/*
14245 * Ext JS Library 1.1.1
14246 * Copyright(c) 2006-2007, Ext JS, LLC.
14248 * Originally Released Under LGPL - original licence link has changed is not relivant.
14251 * <script type="text/javascript">
14256 * @class Roo.data.SortTypes
14258 * Defines the default sorting (casting?) comparison functions used when sorting data.
14260 Roo.data.SortTypes = {
14262 * Default sort that does nothing
14263 * @param {Mixed} s The value being converted
14264 * @return {Mixed} The comparison value
14266 none : function(s){
14271 * The regular expression used to strip tags
14275 stripTagsRE : /<\/?[^>]+>/gi,
14278 * Strips all HTML tags to sort on text only
14279 * @param {Mixed} s The value being converted
14280 * @return {String} The comparison value
14282 asText : function(s){
14283 return String(s).replace(this.stripTagsRE, "");
14287 * Strips all HTML tags to sort on text only - Case insensitive
14288 * @param {Mixed} s The value being converted
14289 * @return {String} The comparison value
14291 asUCText : function(s){
14292 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14296 * Case insensitive string
14297 * @param {Mixed} s The value being converted
14298 * @return {String} The comparison value
14300 asUCString : function(s) {
14301 return String(s).toUpperCase();
14306 * @param {Mixed} s The value being converted
14307 * @return {Number} The comparison value
14309 asDate : function(s) {
14313 if(s instanceof Date){
14314 return s.getTime();
14316 return Date.parse(String(s));
14321 * @param {Mixed} s The value being converted
14322 * @return {Float} The comparison value
14324 asFloat : function(s) {
14325 var val = parseFloat(String(s).replace(/,/g, ""));
14334 * @param {Mixed} s The value being converted
14335 * @return {Number} The comparison value
14337 asInt : function(s) {
14338 var val = parseInt(String(s).replace(/,/g, ""));
14346 * Ext JS Library 1.1.1
14347 * Copyright(c) 2006-2007, Ext JS, LLC.
14349 * Originally Released Under LGPL - original licence link has changed is not relivant.
14352 * <script type="text/javascript">
14356 * @class Roo.data.Record
14357 * Instances of this class encapsulate both record <em>definition</em> information, and record
14358 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14359 * to access Records cached in an {@link Roo.data.Store} object.<br>
14361 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14362 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14365 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14367 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14368 * {@link #create}. The parameters are the same.
14369 * @param {Array} data An associative Array of data values keyed by the field name.
14370 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14371 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14372 * not specified an integer id is generated.
14374 Roo.data.Record = function(data, id){
14375 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14380 * Generate a constructor for a specific record layout.
14381 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14382 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14383 * Each field definition object may contain the following properties: <ul>
14384 * <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,
14385 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14386 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14387 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14388 * is being used, then this is a string containing the javascript expression to reference the data relative to
14389 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14390 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14391 * this may be omitted.</p></li>
14392 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14393 * <ul><li>auto (Default, implies no conversion)</li>
14398 * <li>date</li></ul></p></li>
14399 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14400 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14401 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14402 * by the Reader into an object that will be stored in the Record. It is passed the
14403 * following parameters:<ul>
14404 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14406 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14408 * <br>usage:<br><pre><code>
14409 var TopicRecord = Roo.data.Record.create(
14410 {name: 'title', mapping: 'topic_title'},
14411 {name: 'author', mapping: 'username'},
14412 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14413 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14414 {name: 'lastPoster', mapping: 'user2'},
14415 {name: 'excerpt', mapping: 'post_text'}
14418 var myNewRecord = new TopicRecord({
14419 title: 'Do my job please',
14422 lastPost: new Date(),
14423 lastPoster: 'Animal',
14424 excerpt: 'No way dude!'
14426 myStore.add(myNewRecord);
14431 Roo.data.Record.create = function(o){
14432 var f = function(){
14433 f.superclass.constructor.apply(this, arguments);
14435 Roo.extend(f, Roo.data.Record);
14436 var p = f.prototype;
14437 p.fields = new Roo.util.MixedCollection(false, function(field){
14440 for(var i = 0, len = o.length; i < len; i++){
14441 p.fields.add(new Roo.data.Field(o[i]));
14443 f.getField = function(name){
14444 return p.fields.get(name);
14449 Roo.data.Record.AUTO_ID = 1000;
14450 Roo.data.Record.EDIT = 'edit';
14451 Roo.data.Record.REJECT = 'reject';
14452 Roo.data.Record.COMMIT = 'commit';
14454 Roo.data.Record.prototype = {
14456 * Readonly flag - true if this record has been modified.
14465 join : function(store){
14466 this.store = store;
14470 * Set the named field to the specified value.
14471 * @param {String} name The name of the field to set.
14472 * @param {Object} value The value to set the field to.
14474 set : function(name, value){
14475 if(this.data[name] == value){
14479 if(!this.modified){
14480 this.modified = {};
14482 if(typeof this.modified[name] == 'undefined'){
14483 this.modified[name] = this.data[name];
14485 this.data[name] = value;
14486 if(!this.editing && this.store){
14487 this.store.afterEdit(this);
14492 * Get the value of the named field.
14493 * @param {String} name The name of the field to get the value of.
14494 * @return {Object} The value of the field.
14496 get : function(name){
14497 return this.data[name];
14501 beginEdit : function(){
14502 this.editing = true;
14503 this.modified = {};
14507 cancelEdit : function(){
14508 this.editing = false;
14509 delete this.modified;
14513 endEdit : function(){
14514 this.editing = false;
14515 if(this.dirty && this.store){
14516 this.store.afterEdit(this);
14521 * Usually called by the {@link Roo.data.Store} which owns the Record.
14522 * Rejects all changes made to the Record since either creation, or the last commit operation.
14523 * Modified fields are reverted to their original values.
14525 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14526 * of reject operations.
14528 reject : function(){
14529 var m = this.modified;
14531 if(typeof m[n] != "function"){
14532 this.data[n] = m[n];
14535 this.dirty = false;
14536 delete this.modified;
14537 this.editing = false;
14539 this.store.afterReject(this);
14544 * Usually called by the {@link Roo.data.Store} which owns the Record.
14545 * Commits all changes made to the Record since either creation, or the last commit operation.
14547 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14548 * of commit operations.
14550 commit : function(){
14551 this.dirty = false;
14552 delete this.modified;
14553 this.editing = false;
14555 this.store.afterCommit(this);
14560 hasError : function(){
14561 return this.error != null;
14565 clearError : function(){
14570 * Creates a copy of this record.
14571 * @param {String} id (optional) A new record id if you don't want to use this record's id
14574 copy : function(newId) {
14575 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14579 * Ext JS Library 1.1.1
14580 * Copyright(c) 2006-2007, Ext JS, LLC.
14582 * Originally Released Under LGPL - original licence link has changed is not relivant.
14585 * <script type="text/javascript">
14591 * @class Roo.data.Store
14592 * @extends Roo.util.Observable
14593 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14594 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14596 * 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
14597 * has no knowledge of the format of the data returned by the Proxy.<br>
14599 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14600 * instances from the data object. These records are cached and made available through accessor functions.
14602 * Creates a new Store.
14603 * @param {Object} config A config object containing the objects needed for the Store to access data,
14604 * and read the data into Records.
14606 Roo.data.Store = function(config){
14607 this.data = new Roo.util.MixedCollection(false);
14608 this.data.getKey = function(o){
14611 this.baseParams = {};
14613 this.paramNames = {
14618 "multisort" : "_multisort"
14621 if(config && config.data){
14622 this.inlineData = config.data;
14623 delete config.data;
14626 Roo.apply(this, config);
14628 if(this.reader){ // reader passed
14629 this.reader = Roo.factory(this.reader, Roo.data);
14630 this.reader.xmodule = this.xmodule || false;
14631 if(!this.recordType){
14632 this.recordType = this.reader.recordType;
14634 if(this.reader.onMetaChange){
14635 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14639 if(this.recordType){
14640 this.fields = this.recordType.prototype.fields;
14642 this.modified = [];
14646 * @event datachanged
14647 * Fires when the data cache has changed, and a widget which is using this Store
14648 * as a Record cache should refresh its view.
14649 * @param {Store} this
14651 datachanged : true,
14653 * @event metachange
14654 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14655 * @param {Store} this
14656 * @param {Object} meta The JSON metadata
14661 * Fires when Records have been added to the Store
14662 * @param {Store} this
14663 * @param {Roo.data.Record[]} records The array of Records added
14664 * @param {Number} index The index at which the record(s) were added
14669 * Fires when a Record has been removed from the Store
14670 * @param {Store} this
14671 * @param {Roo.data.Record} record The Record that was removed
14672 * @param {Number} index The index at which the record was removed
14677 * Fires when a Record has been updated
14678 * @param {Store} this
14679 * @param {Roo.data.Record} record The Record that was updated
14680 * @param {String} operation The update operation being performed. Value may be one of:
14682 Roo.data.Record.EDIT
14683 Roo.data.Record.REJECT
14684 Roo.data.Record.COMMIT
14690 * Fires when the data cache has been cleared.
14691 * @param {Store} this
14695 * @event beforeload
14696 * Fires before a request is made for a new data object. If the beforeload handler returns false
14697 * the load action will be canceled.
14698 * @param {Store} this
14699 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14703 * @event beforeloadadd
14704 * Fires after a new set of Records has been loaded.
14705 * @param {Store} this
14706 * @param {Roo.data.Record[]} records The Records that were loaded
14707 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14709 beforeloadadd : true,
14712 * Fires after a new set of Records has been loaded, before they are added to the store.
14713 * @param {Store} this
14714 * @param {Roo.data.Record[]} records The Records that were loaded
14715 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14716 * @params {Object} return from reader
14720 * @event loadexception
14721 * Fires if an exception occurs in the Proxy during loading.
14722 * Called with the signature of the Proxy's "loadexception" event.
14723 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14726 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14727 * @param {Object} load options
14728 * @param {Object} jsonData from your request (normally this contains the Exception)
14730 loadexception : true
14734 this.proxy = Roo.factory(this.proxy, Roo.data);
14735 this.proxy.xmodule = this.xmodule || false;
14736 this.relayEvents(this.proxy, ["loadexception"]);
14738 this.sortToggle = {};
14739 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14741 Roo.data.Store.superclass.constructor.call(this);
14743 if(this.inlineData){
14744 this.loadData(this.inlineData);
14745 delete this.inlineData;
14749 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14751 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14752 * without a remote query - used by combo/forms at present.
14756 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14759 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14762 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14763 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14766 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14767 * on any HTTP request
14770 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14773 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14777 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14778 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14780 remoteSort : false,
14783 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14784 * loaded or when a record is removed. (defaults to false).
14786 pruneModifiedRecords : false,
14789 lastOptions : null,
14792 * Add Records to the Store and fires the add event.
14793 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14795 add : function(records){
14796 records = [].concat(records);
14797 for(var i = 0, len = records.length; i < len; i++){
14798 records[i].join(this);
14800 var index = this.data.length;
14801 this.data.addAll(records);
14802 this.fireEvent("add", this, records, index);
14806 * Remove a Record from the Store and fires the remove event.
14807 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14809 remove : function(record){
14810 var index = this.data.indexOf(record);
14811 this.data.removeAt(index);
14813 if(this.pruneModifiedRecords){
14814 this.modified.remove(record);
14816 this.fireEvent("remove", this, record, index);
14820 * Remove all Records from the Store and fires the clear event.
14822 removeAll : function(){
14824 if(this.pruneModifiedRecords){
14825 this.modified = [];
14827 this.fireEvent("clear", this);
14831 * Inserts Records to the Store at the given index and fires the add event.
14832 * @param {Number} index The start index at which to insert the passed Records.
14833 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14835 insert : function(index, records){
14836 records = [].concat(records);
14837 for(var i = 0, len = records.length; i < len; i++){
14838 this.data.insert(index, records[i]);
14839 records[i].join(this);
14841 this.fireEvent("add", this, records, index);
14845 * Get the index within the cache of the passed Record.
14846 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14847 * @return {Number} The index of the passed Record. Returns -1 if not found.
14849 indexOf : function(record){
14850 return this.data.indexOf(record);
14854 * Get the index within the cache of the Record with the passed id.
14855 * @param {String} id The id of the Record to find.
14856 * @return {Number} The index of the Record. Returns -1 if not found.
14858 indexOfId : function(id){
14859 return this.data.indexOfKey(id);
14863 * Get the Record with the specified id.
14864 * @param {String} id The id of the Record to find.
14865 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14867 getById : function(id){
14868 return this.data.key(id);
14872 * Get the Record at the specified index.
14873 * @param {Number} index The index of the Record to find.
14874 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14876 getAt : function(index){
14877 return this.data.itemAt(index);
14881 * Returns a range of Records between specified indices.
14882 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14883 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14884 * @return {Roo.data.Record[]} An array of Records
14886 getRange : function(start, end){
14887 return this.data.getRange(start, end);
14891 storeOptions : function(o){
14892 o = Roo.apply({}, o);
14895 this.lastOptions = o;
14899 * Loads the Record cache from the configured Proxy using the configured Reader.
14901 * If using remote paging, then the first load call must specify the <em>start</em>
14902 * and <em>limit</em> properties in the options.params property to establish the initial
14903 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14905 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14906 * and this call will return before the new data has been loaded. Perform any post-processing
14907 * in a callback function, or in a "load" event handler.</strong>
14909 * @param {Object} options An object containing properties which control loading options:<ul>
14910 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14911 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14912 * passed the following arguments:<ul>
14913 * <li>r : Roo.data.Record[]</li>
14914 * <li>options: Options object from the load call</li>
14915 * <li>success: Boolean success indicator</li></ul></li>
14916 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14917 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14920 load : function(options){
14921 options = options || {};
14922 if(this.fireEvent("beforeload", this, options) !== false){
14923 this.storeOptions(options);
14924 var p = Roo.apply(options.params || {}, this.baseParams);
14925 // if meta was not loaded from remote source.. try requesting it.
14926 if (!this.reader.metaFromRemote) {
14927 p._requestMeta = 1;
14929 if(this.sortInfo && this.remoteSort){
14930 var pn = this.paramNames;
14931 p[pn["sort"]] = this.sortInfo.field;
14932 p[pn["dir"]] = this.sortInfo.direction;
14934 if (this.multiSort) {
14935 var pn = this.paramNames;
14936 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14939 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14944 * Reloads the Record cache from the configured Proxy using the configured Reader and
14945 * the options from the last load operation performed.
14946 * @param {Object} options (optional) An object containing properties which may override the options
14947 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14948 * the most recently used options are reused).
14950 reload : function(options){
14951 this.load(Roo.applyIf(options||{}, this.lastOptions));
14955 // Called as a callback by the Reader during a load operation.
14956 loadRecords : function(o, options, success){
14957 if(!o || success === false){
14958 if(success !== false){
14959 this.fireEvent("load", this, [], options, o);
14961 if(options.callback){
14962 options.callback.call(options.scope || this, [], options, false);
14966 // if data returned failure - throw an exception.
14967 if (o.success === false) {
14968 // show a message if no listener is registered.
14969 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14970 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14972 // loadmask wil be hooked into this..
14973 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14976 var r = o.records, t = o.totalRecords || r.length;
14978 this.fireEvent("beforeloadadd", this, r, options, o);
14980 if(!options || options.add !== true){
14981 if(this.pruneModifiedRecords){
14982 this.modified = [];
14984 for(var i = 0, len = r.length; i < len; i++){
14988 this.data = this.snapshot;
14989 delete this.snapshot;
14992 this.data.addAll(r);
14993 this.totalLength = t;
14995 this.fireEvent("datachanged", this);
14997 this.totalLength = Math.max(t, this.data.length+r.length);
15001 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15003 var e = new Roo.data.Record({});
15005 e.set(this.parent.displayField, this.parent.emptyTitle);
15006 e.set(this.parent.valueField, '');
15011 this.fireEvent("load", this, r, options, o);
15012 if(options.callback){
15013 options.callback.call(options.scope || this, r, options, true);
15019 * Loads data from a passed data block. A Reader which understands the format of the data
15020 * must have been configured in the constructor.
15021 * @param {Object} data The data block from which to read the Records. The format of the data expected
15022 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15023 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15025 loadData : function(o, append){
15026 var r = this.reader.readRecords(o);
15027 this.loadRecords(r, {add: append}, true);
15031 * using 'cn' the nested child reader read the child array into it's child stores.
15032 * @param {Object} rec The record with a 'children array
15034 loadDataFromChildren : function(rec)
15036 this.loadData(this.reader.toLoadData(rec));
15041 * Gets the number of cached records.
15043 * <em>If using paging, this may not be the total size of the dataset. If the data object
15044 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15045 * the data set size</em>
15047 getCount : function(){
15048 return this.data.length || 0;
15052 * Gets the total number of records in the dataset as returned by the server.
15054 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15055 * the dataset size</em>
15057 getTotalCount : function(){
15058 return this.totalLength || 0;
15062 * Returns the sort state of the Store as an object with two properties:
15064 field {String} The name of the field by which the Records are sorted
15065 direction {String} The sort order, "ASC" or "DESC"
15068 getSortState : function(){
15069 return this.sortInfo;
15073 applySort : function(){
15074 if(this.sortInfo && !this.remoteSort){
15075 var s = this.sortInfo, f = s.field;
15076 var st = this.fields.get(f).sortType;
15077 var fn = function(r1, r2){
15078 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15079 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15081 this.data.sort(s.direction, fn);
15082 if(this.snapshot && this.snapshot != this.data){
15083 this.snapshot.sort(s.direction, fn);
15089 * Sets the default sort column and order to be used by the next load operation.
15090 * @param {String} fieldName The name of the field to sort by.
15091 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15093 setDefaultSort : function(field, dir){
15094 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15098 * Sort the Records.
15099 * If remote sorting is used, the sort is performed on the server, and the cache is
15100 * reloaded. If local sorting is used, the cache is sorted internally.
15101 * @param {String} fieldName The name of the field to sort by.
15102 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15104 sort : function(fieldName, dir){
15105 var f = this.fields.get(fieldName);
15107 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15109 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15110 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15115 this.sortToggle[f.name] = dir;
15116 this.sortInfo = {field: f.name, direction: dir};
15117 if(!this.remoteSort){
15119 this.fireEvent("datachanged", this);
15121 this.load(this.lastOptions);
15126 * Calls the specified function for each of the Records in the cache.
15127 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15128 * Returning <em>false</em> aborts and exits the iteration.
15129 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15131 each : function(fn, scope){
15132 this.data.each(fn, scope);
15136 * Gets all records modified since the last commit. Modified records are persisted across load operations
15137 * (e.g., during paging).
15138 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15140 getModifiedRecords : function(){
15141 return this.modified;
15145 createFilterFn : function(property, value, anyMatch){
15146 if(!value.exec){ // not a regex
15147 value = String(value);
15148 if(value.length == 0){
15151 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15153 return function(r){
15154 return value.test(r.data[property]);
15159 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15160 * @param {String} property A field on your records
15161 * @param {Number} start The record index to start at (defaults to 0)
15162 * @param {Number} end The last record index to include (defaults to length - 1)
15163 * @return {Number} The sum
15165 sum : function(property, start, end){
15166 var rs = this.data.items, v = 0;
15167 start = start || 0;
15168 end = (end || end === 0) ? end : rs.length-1;
15170 for(var i = start; i <= end; i++){
15171 v += (rs[i].data[property] || 0);
15177 * Filter the records by a specified property.
15178 * @param {String} field A field on your records
15179 * @param {String/RegExp} value Either a string that the field
15180 * should start with or a RegExp to test against the field
15181 * @param {Boolean} anyMatch True to match any part not just the beginning
15183 filter : function(property, value, anyMatch){
15184 var fn = this.createFilterFn(property, value, anyMatch);
15185 return fn ? this.filterBy(fn) : this.clearFilter();
15189 * Filter by a function. The specified function will be called with each
15190 * record in this data source. If the function returns true the record is included,
15191 * otherwise it is filtered.
15192 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15193 * @param {Object} scope (optional) The scope of the function (defaults to this)
15195 filterBy : function(fn, scope){
15196 this.snapshot = this.snapshot || this.data;
15197 this.data = this.queryBy(fn, scope||this);
15198 this.fireEvent("datachanged", this);
15202 * Query the records by a specified property.
15203 * @param {String} field A field on your records
15204 * @param {String/RegExp} value Either a string that the field
15205 * should start with or a RegExp to test against the field
15206 * @param {Boolean} anyMatch True to match any part not just the beginning
15207 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15209 query : function(property, value, anyMatch){
15210 var fn = this.createFilterFn(property, value, anyMatch);
15211 return fn ? this.queryBy(fn) : this.data.clone();
15215 * Query by a function. The specified function will be called with each
15216 * record in this data source. If the function returns true the record is included
15218 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15219 * @param {Object} scope (optional) The scope of the function (defaults to this)
15220 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15222 queryBy : function(fn, scope){
15223 var data = this.snapshot || this.data;
15224 return data.filterBy(fn, scope||this);
15228 * Collects unique values for a particular dataIndex from this store.
15229 * @param {String} dataIndex The property to collect
15230 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15231 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15232 * @return {Array} An array of the unique values
15234 collect : function(dataIndex, allowNull, bypassFilter){
15235 var d = (bypassFilter === true && this.snapshot) ?
15236 this.snapshot.items : this.data.items;
15237 var v, sv, r = [], l = {};
15238 for(var i = 0, len = d.length; i < len; i++){
15239 v = d[i].data[dataIndex];
15241 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15250 * Revert to a view of the Record cache with no filtering applied.
15251 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15253 clearFilter : function(suppressEvent){
15254 if(this.snapshot && this.snapshot != this.data){
15255 this.data = this.snapshot;
15256 delete this.snapshot;
15257 if(suppressEvent !== true){
15258 this.fireEvent("datachanged", this);
15264 afterEdit : function(record){
15265 if(this.modified.indexOf(record) == -1){
15266 this.modified.push(record);
15268 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15272 afterReject : function(record){
15273 this.modified.remove(record);
15274 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15278 afterCommit : function(record){
15279 this.modified.remove(record);
15280 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15284 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15285 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15287 commitChanges : function(){
15288 var m = this.modified.slice(0);
15289 this.modified = [];
15290 for(var i = 0, len = m.length; i < len; i++){
15296 * Cancel outstanding changes on all changed records.
15298 rejectChanges : function(){
15299 var m = this.modified.slice(0);
15300 this.modified = [];
15301 for(var i = 0, len = m.length; i < len; i++){
15306 onMetaChange : function(meta, rtype, o){
15307 this.recordType = rtype;
15308 this.fields = rtype.prototype.fields;
15309 delete this.snapshot;
15310 this.sortInfo = meta.sortInfo || this.sortInfo;
15311 this.modified = [];
15312 this.fireEvent('metachange', this, this.reader.meta);
15315 moveIndex : function(data, type)
15317 var index = this.indexOf(data);
15319 var newIndex = index + type;
15323 this.insert(newIndex, data);
15328 * Ext JS Library 1.1.1
15329 * Copyright(c) 2006-2007, Ext JS, LLC.
15331 * Originally Released Under LGPL - original licence link has changed is not relivant.
15334 * <script type="text/javascript">
15338 * @class Roo.data.SimpleStore
15339 * @extends Roo.data.Store
15340 * Small helper class to make creating Stores from Array data easier.
15341 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15342 * @cfg {Array} fields An array of field definition objects, or field name strings.
15343 * @cfg {Object} an existing reader (eg. copied from another store)
15344 * @cfg {Array} data The multi-dimensional array of data
15346 * @param {Object} config
15348 Roo.data.SimpleStore = function(config)
15350 Roo.data.SimpleStore.superclass.constructor.call(this, {
15352 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15355 Roo.data.Record.create(config.fields)
15357 proxy : new Roo.data.MemoryProxy(config.data)
15361 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15363 * Ext JS Library 1.1.1
15364 * Copyright(c) 2006-2007, Ext JS, LLC.
15366 * Originally Released Under LGPL - original licence link has changed is not relivant.
15369 * <script type="text/javascript">
15374 * @extends Roo.data.Store
15375 * @class Roo.data.JsonStore
15376 * Small helper class to make creating Stores for JSON data easier. <br/>
15378 var store = new Roo.data.JsonStore({
15379 url: 'get-images.php',
15381 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15384 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15385 * JsonReader and HttpProxy (unless inline data is provided).</b>
15386 * @cfg {Array} fields An array of field definition objects, or field name strings.
15388 * @param {Object} config
15390 Roo.data.JsonStore = function(c){
15391 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15392 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15393 reader: new Roo.data.JsonReader(c, c.fields)
15396 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15398 * Ext JS Library 1.1.1
15399 * Copyright(c) 2006-2007, Ext JS, LLC.
15401 * Originally Released Under LGPL - original licence link has changed is not relivant.
15404 * <script type="text/javascript">
15408 Roo.data.Field = function(config){
15409 if(typeof config == "string"){
15410 config = {name: config};
15412 Roo.apply(this, config);
15415 this.type = "auto";
15418 var st = Roo.data.SortTypes;
15419 // named sortTypes are supported, here we look them up
15420 if(typeof this.sortType == "string"){
15421 this.sortType = st[this.sortType];
15424 // set default sortType for strings and dates
15425 if(!this.sortType){
15428 this.sortType = st.asUCString;
15431 this.sortType = st.asDate;
15434 this.sortType = st.none;
15439 var stripRe = /[\$,%]/g;
15441 // prebuilt conversion function for this field, instead of
15442 // switching every time we're reading a value
15444 var cv, dateFormat = this.dateFormat;
15449 cv = function(v){ return v; };
15452 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15456 return v !== undefined && v !== null && v !== '' ?
15457 parseInt(String(v).replace(stripRe, ""), 10) : '';
15462 return v !== undefined && v !== null && v !== '' ?
15463 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15468 cv = function(v){ return v === true || v === "true" || v == 1; };
15475 if(v instanceof Date){
15479 if(dateFormat == "timestamp"){
15480 return new Date(v*1000);
15482 return Date.parseDate(v, dateFormat);
15484 var parsed = Date.parse(v);
15485 return parsed ? new Date(parsed) : null;
15494 Roo.data.Field.prototype = {
15502 * Ext JS Library 1.1.1
15503 * Copyright(c) 2006-2007, Ext JS, LLC.
15505 * Originally Released Under LGPL - original licence link has changed is not relivant.
15508 * <script type="text/javascript">
15511 // Base class for reading structured data from a data source. This class is intended to be
15512 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15515 * @class Roo.data.DataReader
15516 * Base class for reading structured data from a data source. This class is intended to be
15517 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15520 Roo.data.DataReader = function(meta, recordType){
15524 this.recordType = recordType instanceof Array ?
15525 Roo.data.Record.create(recordType) : recordType;
15528 Roo.data.DataReader.prototype = {
15531 readerType : 'Data',
15533 * Create an empty record
15534 * @param {Object} data (optional) - overlay some values
15535 * @return {Roo.data.Record} record created.
15537 newRow : function(d) {
15539 this.recordType.prototype.fields.each(function(c) {
15541 case 'int' : da[c.name] = 0; break;
15542 case 'date' : da[c.name] = new Date(); break;
15543 case 'float' : da[c.name] = 0.0; break;
15544 case 'boolean' : da[c.name] = false; break;
15545 default : da[c.name] = ""; break;
15549 return new this.recordType(Roo.apply(da, d));
15555 * Ext JS Library 1.1.1
15556 * Copyright(c) 2006-2007, Ext JS, LLC.
15558 * Originally Released Under LGPL - original licence link has changed is not relivant.
15561 * <script type="text/javascript">
15565 * @class Roo.data.DataProxy
15566 * @extends Roo.data.Observable
15567 * This class is an abstract base class for implementations which provide retrieval of
15568 * unformatted data objects.<br>
15570 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15571 * (of the appropriate type which knows how to parse the data object) to provide a block of
15572 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15574 * Custom implementations must implement the load method as described in
15575 * {@link Roo.data.HttpProxy#load}.
15577 Roo.data.DataProxy = function(){
15580 * @event beforeload
15581 * Fires before a network request is made to retrieve a data object.
15582 * @param {Object} This DataProxy object.
15583 * @param {Object} params The params parameter to the load function.
15588 * Fires before the load method's callback is called.
15589 * @param {Object} This DataProxy object.
15590 * @param {Object} o The data object.
15591 * @param {Object} arg The callback argument object passed to the load function.
15595 * @event loadexception
15596 * Fires if an Exception occurs during data retrieval.
15597 * @param {Object} This DataProxy object.
15598 * @param {Object} o The data object.
15599 * @param {Object} arg The callback argument object passed to the load function.
15600 * @param {Object} e The Exception.
15602 loadexception : true
15604 Roo.data.DataProxy.superclass.constructor.call(this);
15607 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15610 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15614 * Ext JS Library 1.1.1
15615 * Copyright(c) 2006-2007, Ext JS, LLC.
15617 * Originally Released Under LGPL - original licence link has changed is not relivant.
15620 * <script type="text/javascript">
15623 * @class Roo.data.MemoryProxy
15624 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15625 * to the Reader when its load method is called.
15627 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15629 Roo.data.MemoryProxy = function(data){
15633 Roo.data.MemoryProxy.superclass.constructor.call(this);
15637 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15640 * Load data from the requested source (in this case an in-memory
15641 * data object passed to the constructor), read the data object into
15642 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15643 * process that block using the passed callback.
15644 * @param {Object} params This parameter is not used by the MemoryProxy class.
15645 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15646 * object into a block of Roo.data.Records.
15647 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15648 * The function must be passed <ul>
15649 * <li>The Record block object</li>
15650 * <li>The "arg" argument from the load function</li>
15651 * <li>A boolean success indicator</li>
15653 * @param {Object} scope The scope in which to call the callback
15654 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15656 load : function(params, reader, callback, scope, arg){
15657 params = params || {};
15660 result = reader.readRecords(params.data ? params.data :this.data);
15662 this.fireEvent("loadexception", this, arg, null, e);
15663 callback.call(scope, null, arg, false);
15666 callback.call(scope, result, arg, true);
15670 update : function(params, records){
15675 * Ext JS Library 1.1.1
15676 * Copyright(c) 2006-2007, Ext JS, LLC.
15678 * Originally Released Under LGPL - original licence link has changed is not relivant.
15681 * <script type="text/javascript">
15684 * @class Roo.data.HttpProxy
15685 * @extends Roo.data.DataProxy
15686 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15687 * configured to reference a certain URL.<br><br>
15689 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15690 * from which the running page was served.<br><br>
15692 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15694 * Be aware that to enable the browser to parse an XML document, the server must set
15695 * the Content-Type header in the HTTP response to "text/xml".
15697 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15698 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15699 * will be used to make the request.
15701 Roo.data.HttpProxy = function(conn){
15702 Roo.data.HttpProxy.superclass.constructor.call(this);
15703 // is conn a conn config or a real conn?
15705 this.useAjax = !conn || !conn.events;
15709 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15710 // thse are take from connection...
15713 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15716 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15717 * extra parameters to each request made by this object. (defaults to undefined)
15720 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15721 * to each request made by this object. (defaults to undefined)
15724 * @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)
15727 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15730 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15736 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15740 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15741 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15742 * a finer-grained basis than the DataProxy events.
15744 getConnection : function(){
15745 return this.useAjax ? Roo.Ajax : this.conn;
15749 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15750 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15751 * process that block using the passed callback.
15752 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15753 * for the request to the remote server.
15754 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15755 * object into a block of Roo.data.Records.
15756 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15757 * The function must be passed <ul>
15758 * <li>The Record block object</li>
15759 * <li>The "arg" argument from the load function</li>
15760 * <li>A boolean success indicator</li>
15762 * @param {Object} scope The scope in which to call the callback
15763 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15765 load : function(params, reader, callback, scope, arg){
15766 if(this.fireEvent("beforeload", this, params) !== false){
15768 params : params || {},
15770 callback : callback,
15775 callback : this.loadResponse,
15779 Roo.applyIf(o, this.conn);
15780 if(this.activeRequest){
15781 Roo.Ajax.abort(this.activeRequest);
15783 this.activeRequest = Roo.Ajax.request(o);
15785 this.conn.request(o);
15788 callback.call(scope||this, null, arg, false);
15793 loadResponse : function(o, success, response){
15794 delete this.activeRequest;
15796 this.fireEvent("loadexception", this, o, response);
15797 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15802 result = o.reader.read(response);
15804 this.fireEvent("loadexception", this, o, response, e);
15805 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15809 this.fireEvent("load", this, o, o.request.arg);
15810 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15814 update : function(dataSet){
15819 updateResponse : function(dataSet){
15824 * Ext JS Library 1.1.1
15825 * Copyright(c) 2006-2007, Ext JS, LLC.
15827 * Originally Released Under LGPL - original licence link has changed is not relivant.
15830 * <script type="text/javascript">
15834 * @class Roo.data.ScriptTagProxy
15835 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15836 * other than the originating domain of the running page.<br><br>
15838 * <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
15839 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15841 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15842 * source code that is used as the source inside a <script> tag.<br><br>
15844 * In order for the browser to process the returned data, the server must wrap the data object
15845 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15846 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15847 * depending on whether the callback name was passed:
15850 boolean scriptTag = false;
15851 String cb = request.getParameter("callback");
15854 response.setContentType("text/javascript");
15856 response.setContentType("application/x-json");
15858 Writer out = response.getWriter();
15860 out.write(cb + "(");
15862 out.print(dataBlock.toJsonString());
15869 * @param {Object} config A configuration object.
15871 Roo.data.ScriptTagProxy = function(config){
15872 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15873 Roo.apply(this, config);
15874 this.head = document.getElementsByTagName("head")[0];
15877 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15879 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15881 * @cfg {String} url The URL from which to request the data object.
15884 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15888 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15889 * the server the name of the callback function set up by the load call to process the returned data object.
15890 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15891 * javascript output which calls this named function passing the data object as its only parameter.
15893 callbackParam : "callback",
15895 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15896 * name to the request.
15901 * Load data from the configured URL, read the data object into
15902 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15903 * process that block using the passed callback.
15904 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15905 * for the request to the remote server.
15906 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15907 * object into a block of Roo.data.Records.
15908 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15909 * The function must be passed <ul>
15910 * <li>The Record block object</li>
15911 * <li>The "arg" argument from the load function</li>
15912 * <li>A boolean success indicator</li>
15914 * @param {Object} scope The scope in which to call the callback
15915 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15917 load : function(params, reader, callback, scope, arg){
15918 if(this.fireEvent("beforeload", this, params) !== false){
15920 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15922 var url = this.url;
15923 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15925 url += "&_dc=" + (new Date().getTime());
15927 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15930 cb : "stcCallback"+transId,
15931 scriptId : "stcScript"+transId,
15935 callback : callback,
15941 window[trans.cb] = function(o){
15942 conn.handleResponse(o, trans);
15945 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15947 if(this.autoAbort !== false){
15951 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15953 var script = document.createElement("script");
15954 script.setAttribute("src", url);
15955 script.setAttribute("type", "text/javascript");
15956 script.setAttribute("id", trans.scriptId);
15957 this.head.appendChild(script);
15959 this.trans = trans;
15961 callback.call(scope||this, null, arg, false);
15966 isLoading : function(){
15967 return this.trans ? true : false;
15971 * Abort the current server request.
15973 abort : function(){
15974 if(this.isLoading()){
15975 this.destroyTrans(this.trans);
15980 destroyTrans : function(trans, isLoaded){
15981 this.head.removeChild(document.getElementById(trans.scriptId));
15982 clearTimeout(trans.timeoutId);
15984 window[trans.cb] = undefined;
15986 delete window[trans.cb];
15989 // if hasn't been loaded, wait for load to remove it to prevent script error
15990 window[trans.cb] = function(){
15991 window[trans.cb] = undefined;
15993 delete window[trans.cb];
16000 handleResponse : function(o, trans){
16001 this.trans = false;
16002 this.destroyTrans(trans, true);
16005 result = trans.reader.readRecords(o);
16007 this.fireEvent("loadexception", this, o, trans.arg, e);
16008 trans.callback.call(trans.scope||window, null, trans.arg, false);
16011 this.fireEvent("load", this, o, trans.arg);
16012 trans.callback.call(trans.scope||window, result, trans.arg, true);
16016 handleFailure : function(trans){
16017 this.trans = false;
16018 this.destroyTrans(trans, false);
16019 this.fireEvent("loadexception", this, null, trans.arg);
16020 trans.callback.call(trans.scope||window, null, trans.arg, false);
16024 * Ext JS Library 1.1.1
16025 * Copyright(c) 2006-2007, Ext JS, LLC.
16027 * Originally Released Under LGPL - original licence link has changed is not relivant.
16030 * <script type="text/javascript">
16034 * @class Roo.data.JsonReader
16035 * @extends Roo.data.DataReader
16036 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16037 * based on mappings in a provided Roo.data.Record constructor.
16039 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16040 * in the reply previously.
16045 var RecordDef = Roo.data.Record.create([
16046 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16047 {name: 'occupation'} // This field will use "occupation" as the mapping.
16049 var myReader = new Roo.data.JsonReader({
16050 totalProperty: "results", // The property which contains the total dataset size (optional)
16051 root: "rows", // The property which contains an Array of row objects
16052 id: "id" // The property within each row object that provides an ID for the record (optional)
16056 * This would consume a JSON file like this:
16058 { 'results': 2, 'rows': [
16059 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16060 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16063 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16064 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16065 * paged from the remote server.
16066 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16067 * @cfg {String} root name of the property which contains the Array of row objects.
16068 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16069 * @cfg {Array} fields Array of field definition objects
16071 * Create a new JsonReader
16072 * @param {Object} meta Metadata configuration options
16073 * @param {Object} recordType Either an Array of field definition objects,
16074 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16076 Roo.data.JsonReader = function(meta, recordType){
16079 // set some defaults:
16080 Roo.applyIf(meta, {
16081 totalProperty: 'total',
16082 successProperty : 'success',
16087 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16089 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16091 readerType : 'Json',
16094 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16095 * Used by Store query builder to append _requestMeta to params.
16098 metaFromRemote : false,
16100 * This method is only used by a DataProxy which has retrieved data from a remote server.
16101 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16102 * @return {Object} data A data block which is used by an Roo.data.Store object as
16103 * a cache of Roo.data.Records.
16105 read : function(response){
16106 var json = response.responseText;
16108 var o = /* eval:var:o */ eval("("+json+")");
16110 throw {message: "JsonReader.read: Json object not found"};
16116 this.metaFromRemote = true;
16117 this.meta = o.metaData;
16118 this.recordType = Roo.data.Record.create(o.metaData.fields);
16119 this.onMetaChange(this.meta, this.recordType, o);
16121 return this.readRecords(o);
16124 // private function a store will implement
16125 onMetaChange : function(meta, recordType, o){
16132 simpleAccess: function(obj, subsc) {
16139 getJsonAccessor: function(){
16141 return function(expr) {
16143 return(re.test(expr))
16144 ? new Function("obj", "return obj." + expr)
16149 return Roo.emptyFn;
16154 * Create a data block containing Roo.data.Records from an XML document.
16155 * @param {Object} o An object which contains an Array of row objects in the property specified
16156 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16157 * which contains the total size of the dataset.
16158 * @return {Object} data A data block which is used by an Roo.data.Store object as
16159 * a cache of Roo.data.Records.
16161 readRecords : function(o){
16163 * After any data loads, the raw JSON data is available for further custom processing.
16167 var s = this.meta, Record = this.recordType,
16168 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16170 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16172 if(s.totalProperty) {
16173 this.getTotal = this.getJsonAccessor(s.totalProperty);
16175 if(s.successProperty) {
16176 this.getSuccess = this.getJsonAccessor(s.successProperty);
16178 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16180 var g = this.getJsonAccessor(s.id);
16181 this.getId = function(rec) {
16183 return (r === undefined || r === "") ? null : r;
16186 this.getId = function(){return null;};
16189 for(var jj = 0; jj < fl; jj++){
16191 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16192 this.ef[jj] = this.getJsonAccessor(map);
16196 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16197 if(s.totalProperty){
16198 var vt = parseInt(this.getTotal(o), 10);
16203 if(s.successProperty){
16204 var vs = this.getSuccess(o);
16205 if(vs === false || vs === 'false'){
16210 for(var i = 0; i < c; i++){
16213 var id = this.getId(n);
16214 for(var j = 0; j < fl; j++){
16216 var v = this.ef[j](n);
16218 Roo.log('missing convert for ' + f.name);
16222 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16224 var record = new Record(values, id);
16226 records[i] = record;
16232 totalRecords : totalRecords
16235 // used when loading children.. @see loadDataFromChildren
16236 toLoadData: function(rec)
16238 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16239 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16240 return { data : data, total : data.length };
16245 * Ext JS Library 1.1.1
16246 * Copyright(c) 2006-2007, Ext JS, LLC.
16248 * Originally Released Under LGPL - original licence link has changed is not relivant.
16251 * <script type="text/javascript">
16255 * @class Roo.data.ArrayReader
16256 * @extends Roo.data.DataReader
16257 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16258 * Each element of that Array represents a row of data fields. The
16259 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16260 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16264 var RecordDef = Roo.data.Record.create([
16265 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16266 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16268 var myReader = new Roo.data.ArrayReader({
16269 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16273 * This would consume an Array like this:
16275 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16279 * Create a new JsonReader
16280 * @param {Object} meta Metadata configuration options.
16281 * @param {Object|Array} recordType Either an Array of field definition objects
16283 * @cfg {Array} fields Array of field definition objects
16284 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16285 * as specified to {@link Roo.data.Record#create},
16286 * or an {@link Roo.data.Record} object
16289 * created using {@link Roo.data.Record#create}.
16291 Roo.data.ArrayReader = function(meta, recordType)
16293 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16296 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16299 * Create a data block containing Roo.data.Records from an XML document.
16300 * @param {Object} o An Array of row objects which represents the dataset.
16301 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16302 * a cache of Roo.data.Records.
16304 readRecords : function(o)
16306 var sid = this.meta ? this.meta.id : null;
16307 var recordType = this.recordType, fields = recordType.prototype.fields;
16310 for(var i = 0; i < root.length; i++){
16313 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16314 for(var j = 0, jlen = fields.length; j < jlen; j++){
16315 var f = fields.items[j];
16316 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16317 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16319 values[f.name] = v;
16321 var record = new recordType(values, id);
16323 records[records.length] = record;
16327 totalRecords : records.length
16330 // used when loading children.. @see loadDataFromChildren
16331 toLoadData: function(rec)
16333 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16334 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16345 * @class Roo.bootstrap.ComboBox
16346 * @extends Roo.bootstrap.TriggerField
16347 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16348 * @cfg {Boolean} append (true|false) default false
16349 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16350 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16351 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16352 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16353 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16354 * @cfg {Boolean} animate default true
16355 * @cfg {Boolean} emptyResultText only for touch device
16356 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16357 * @cfg {String} emptyTitle default ''
16358 * @cfg {Number} width fixed with? experimental
16360 * Create a new ComboBox.
16361 * @param {Object} config Configuration options
16363 Roo.bootstrap.ComboBox = function(config){
16364 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16368 * Fires when the dropdown list is expanded
16369 * @param {Roo.bootstrap.ComboBox} combo This combo box
16374 * Fires when the dropdown list is collapsed
16375 * @param {Roo.bootstrap.ComboBox} combo This combo box
16379 * @event beforeselect
16380 * Fires before a list item is selected. Return false to cancel the selection.
16381 * @param {Roo.bootstrap.ComboBox} combo This combo box
16382 * @param {Roo.data.Record} record The data record returned from the underlying store
16383 * @param {Number} index The index of the selected item in the dropdown list
16385 'beforeselect' : true,
16388 * Fires when a list item is selected
16389 * @param {Roo.bootstrap.ComboBox} combo This combo box
16390 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16391 * @param {Number} index The index of the selected item in the dropdown list
16395 * @event beforequery
16396 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16397 * The event object passed has these properties:
16398 * @param {Roo.bootstrap.ComboBox} combo This combo box
16399 * @param {String} query The query
16400 * @param {Boolean} forceAll true to force "all" query
16401 * @param {Boolean} cancel true to cancel the query
16402 * @param {Object} e The query event object
16404 'beforequery': true,
16407 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16408 * @param {Roo.bootstrap.ComboBox} combo This combo box
16413 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16414 * @param {Roo.bootstrap.ComboBox} combo This combo box
16415 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16420 * Fires when the remove value from the combobox array
16421 * @param {Roo.bootstrap.ComboBox} combo This combo box
16425 * @event afterremove
16426 * Fires when the remove value from the combobox array
16427 * @param {Roo.bootstrap.ComboBox} combo This combo box
16429 'afterremove' : true,
16431 * @event specialfilter
16432 * Fires when specialfilter
16433 * @param {Roo.bootstrap.ComboBox} combo This combo box
16435 'specialfilter' : true,
16438 * Fires when tick the element
16439 * @param {Roo.bootstrap.ComboBox} combo This combo box
16443 * @event touchviewdisplay
16444 * Fires when touch view require special display (default is using displayField)
16445 * @param {Roo.bootstrap.ComboBox} combo This combo box
16446 * @param {Object} cfg set html .
16448 'touchviewdisplay' : true
16453 this.tickItems = [];
16455 this.selectedIndex = -1;
16456 if(this.mode == 'local'){
16457 if(config.queryDelay === undefined){
16458 this.queryDelay = 10;
16460 if(config.minChars === undefined){
16466 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16469 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16470 * rendering into an Roo.Editor, defaults to false)
16473 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16474 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16477 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16480 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16481 * the dropdown list (defaults to undefined, with no header element)
16485 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16489 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16491 listWidth: undefined,
16493 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16494 * mode = 'remote' or 'text' if mode = 'local')
16496 displayField: undefined,
16499 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16500 * mode = 'remote' or 'value' if mode = 'local').
16501 * Note: use of a valueField requires the user make a selection
16502 * in order for a value to be mapped.
16504 valueField: undefined,
16506 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16511 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16512 * field's data value (defaults to the underlying DOM element's name)
16514 hiddenName: undefined,
16516 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16520 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16522 selectedClass: 'active',
16525 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16529 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16530 * anchor positions (defaults to 'tl-bl')
16532 listAlign: 'tl-bl?',
16534 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16538 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16539 * query specified by the allQuery config option (defaults to 'query')
16541 triggerAction: 'query',
16543 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16544 * (defaults to 4, does not apply if editable = false)
16548 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16549 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16553 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16554 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16558 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16559 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16563 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16564 * when editable = true (defaults to false)
16566 selectOnFocus:false,
16568 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16570 queryParam: 'query',
16572 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16573 * when mode = 'remote' (defaults to 'Loading...')
16575 loadingText: 'Loading...',
16577 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16581 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16585 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16586 * traditional select (defaults to true)
16590 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16594 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16598 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16599 * listWidth has a higher value)
16603 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16604 * allow the user to set arbitrary text into the field (defaults to false)
16606 forceSelection:false,
16608 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16609 * if typeAhead = true (defaults to 250)
16611 typeAheadDelay : 250,
16613 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16614 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16616 valueNotFoundText : undefined,
16618 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16620 blockFocus : false,
16623 * @cfg {Boolean} disableClear Disable showing of clear button.
16625 disableClear : false,
16627 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16629 alwaysQuery : false,
16632 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16637 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16639 invalidClass : "has-warning",
16642 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16644 validClass : "has-success",
16647 * @cfg {Boolean} specialFilter (true|false) special filter default false
16649 specialFilter : false,
16652 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16654 mobileTouchView : true,
16657 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16659 useNativeIOS : false,
16662 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16664 mobile_restrict_height : false,
16666 ios_options : false,
16678 btnPosition : 'right',
16679 triggerList : true,
16680 showToggleBtn : true,
16682 emptyResultText: 'Empty',
16683 triggerText : 'Select',
16687 // element that contains real text value.. (when hidden is used..)
16689 getAutoCreate : function()
16694 * Render classic select for iso
16697 if(Roo.isIOS && this.useNativeIOS){
16698 cfg = this.getAutoCreateNativeIOS();
16706 if(Roo.isTouch && this.mobileTouchView){
16707 cfg = this.getAutoCreateTouchView();
16714 if(!this.tickable){
16715 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16720 * ComboBox with tickable selections
16723 var align = this.labelAlign || this.parentLabelAlign();
16726 cls : 'form-group roo-combobox-tickable' //input-group
16729 var btn_text_select = '';
16730 var btn_text_done = '';
16731 var btn_text_cancel = '';
16733 if (this.btn_text_show) {
16734 btn_text_select = 'Select';
16735 btn_text_done = 'Done';
16736 btn_text_cancel = 'Cancel';
16741 cls : 'tickable-buttons',
16746 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16747 //html : this.triggerText
16748 html: btn_text_select
16754 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16756 html: btn_text_done
16762 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16764 html: btn_text_cancel
16770 buttons.cn.unshift({
16772 cls: 'roo-select2-search-field-input'
16778 Roo.each(buttons.cn, function(c){
16780 c.cls += ' btn-' + _this.size;
16783 if (_this.disabled) {
16790 style : 'display: contents',
16795 cls: 'form-hidden-field'
16799 cls: 'roo-select2-choices',
16803 cls: 'roo-select2-search-field',
16814 cls: 'roo-select2-container input-group roo-select2-container-multi',
16820 // cls: 'typeahead typeahead-long dropdown-menu',
16821 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16826 if(this.hasFeedback && !this.allowBlank){
16830 cls: 'glyphicon form-control-feedback'
16833 combobox.cn.push(feedback);
16840 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16841 tooltip : 'This field is required'
16843 if (Roo.bootstrap.version == 4) {
16846 style : 'display:none'
16849 if (align ==='left' && this.fieldLabel.length) {
16851 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16858 cls : 'control-label col-form-label',
16859 html : this.fieldLabel
16871 var labelCfg = cfg.cn[1];
16872 var contentCfg = cfg.cn[2];
16875 if(this.indicatorpos == 'right'){
16881 cls : 'control-label col-form-label',
16885 html : this.fieldLabel
16901 labelCfg = cfg.cn[0];
16902 contentCfg = cfg.cn[1];
16906 if(this.labelWidth > 12){
16907 labelCfg.style = "width: " + this.labelWidth + 'px';
16909 if(this.width * 1 > 0){
16910 contentCfg.style = "width: " + this.width + 'px';
16912 if(this.labelWidth < 13 && this.labelmd == 0){
16913 this.labelmd = this.labelWidth;
16916 if(this.labellg > 0){
16917 labelCfg.cls += ' col-lg-' + this.labellg;
16918 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16921 if(this.labelmd > 0){
16922 labelCfg.cls += ' col-md-' + this.labelmd;
16923 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16926 if(this.labelsm > 0){
16927 labelCfg.cls += ' col-sm-' + this.labelsm;
16928 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16931 if(this.labelxs > 0){
16932 labelCfg.cls += ' col-xs-' + this.labelxs;
16933 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16937 } else if ( this.fieldLabel.length) {
16938 // Roo.log(" label");
16943 //cls : 'input-group-addon',
16944 html : this.fieldLabel
16949 if(this.indicatorpos == 'right'){
16953 //cls : 'input-group-addon',
16954 html : this.fieldLabel
16964 // Roo.log(" no label && no align");
16971 ['xs','sm','md','lg'].map(function(size){
16972 if (settings[size]) {
16973 cfg.cls += ' col-' + size + '-' + settings[size];
16981 _initEventsCalled : false,
16984 initEvents: function()
16986 if (this._initEventsCalled) { // as we call render... prevent looping...
16989 this._initEventsCalled = true;
16992 throw "can not find store for combo";
16995 this.indicator = this.indicatorEl();
16997 this.store = Roo.factory(this.store, Roo.data);
16998 this.store.parent = this;
17000 // if we are building from html. then this element is so complex, that we can not really
17001 // use the rendered HTML.
17002 // so we have to trash and replace the previous code.
17003 if (Roo.XComponent.build_from_html) {
17004 // remove this element....
17005 var e = this.el.dom, k=0;
17006 while (e ) { e = e.previousSibling; ++k;}
17011 this.rendered = false;
17013 this.render(this.parent().getChildContainer(true), k);
17016 if(Roo.isIOS && this.useNativeIOS){
17017 this.initIOSView();
17025 if(Roo.isTouch && this.mobileTouchView){
17026 this.initTouchView();
17031 this.initTickableEvents();
17035 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17037 if(this.hiddenName){
17039 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17041 this.hiddenField.dom.value =
17042 this.hiddenValue !== undefined ? this.hiddenValue :
17043 this.value !== undefined ? this.value : '';
17045 // prevent input submission
17046 this.el.dom.removeAttribute('name');
17047 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17052 // this.el.dom.setAttribute('autocomplete', 'off');
17055 var cls = 'x-combo-list';
17057 //this.list = new Roo.Layer({
17058 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17064 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17065 _this.list.setWidth(lw);
17068 this.list.on('mouseover', this.onViewOver, this);
17069 this.list.on('mousemove', this.onViewMove, this);
17070 this.list.on('scroll', this.onViewScroll, this);
17073 this.list.swallowEvent('mousewheel');
17074 this.assetHeight = 0;
17077 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17078 this.assetHeight += this.header.getHeight();
17081 this.innerList = this.list.createChild({cls:cls+'-inner'});
17082 this.innerList.on('mouseover', this.onViewOver, this);
17083 this.innerList.on('mousemove', this.onViewMove, this);
17084 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17086 if(this.allowBlank && !this.pageSize && !this.disableClear){
17087 this.footer = this.list.createChild({cls:cls+'-ft'});
17088 this.pageTb = new Roo.Toolbar(this.footer);
17092 this.footer = this.list.createChild({cls:cls+'-ft'});
17093 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17094 {pageSize: this.pageSize});
17098 if (this.pageTb && this.allowBlank && !this.disableClear) {
17100 this.pageTb.add(new Roo.Toolbar.Fill(), {
17101 cls: 'x-btn-icon x-btn-clear',
17103 handler: function()
17106 _this.clearValue();
17107 _this.onSelect(false, -1);
17112 this.assetHeight += this.footer.getHeight();
17117 this.tpl = Roo.bootstrap.version == 4 ?
17118 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17119 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17122 this.view = new Roo.View(this.list, this.tpl, {
17123 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17125 //this.view.wrapEl.setDisplayed(false);
17126 this.view.on('click', this.onViewClick, this);
17129 this.store.on('beforeload', this.onBeforeLoad, this);
17130 this.store.on('load', this.onLoad, this);
17131 this.store.on('loadexception', this.onLoadException, this);
17133 if(this.resizable){
17134 this.resizer = new Roo.Resizable(this.list, {
17135 pinned:true, handles:'se'
17137 this.resizer.on('resize', function(r, w, h){
17138 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17139 this.listWidth = w;
17140 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17141 this.restrictHeight();
17143 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17146 if(!this.editable){
17147 this.editable = true;
17148 this.setEditable(false);
17153 if (typeof(this.events.add.listeners) != 'undefined') {
17155 this.addicon = this.wrap.createChild(
17156 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17158 this.addicon.on('click', function(e) {
17159 this.fireEvent('add', this);
17162 if (typeof(this.events.edit.listeners) != 'undefined') {
17164 this.editicon = this.wrap.createChild(
17165 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17166 if (this.addicon) {
17167 this.editicon.setStyle('margin-left', '40px');
17169 this.editicon.on('click', function(e) {
17171 // we fire even if inothing is selected..
17172 this.fireEvent('edit', this, this.lastData );
17178 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17179 "up" : function(e){
17180 this.inKeyMode = true;
17184 "down" : function(e){
17185 if(!this.isExpanded()){
17186 this.onTriggerClick();
17188 this.inKeyMode = true;
17193 "enter" : function(e){
17194 // this.onViewClick();
17198 if(this.fireEvent("specialkey", this, e)){
17199 this.onViewClick(false);
17205 "esc" : function(e){
17209 "tab" : function(e){
17212 if(this.fireEvent("specialkey", this, e)){
17213 this.onViewClick(false);
17221 doRelay : function(foo, bar, hname){
17222 if(hname == 'down' || this.scope.isExpanded()){
17223 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17232 this.queryDelay = Math.max(this.queryDelay || 10,
17233 this.mode == 'local' ? 10 : 250);
17236 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17238 if(this.typeAhead){
17239 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17241 if(this.editable !== false){
17242 this.inputEl().on("keyup", this.onKeyUp, this);
17244 if(this.forceSelection){
17245 this.inputEl().on('blur', this.doForce, this);
17249 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17250 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17254 initTickableEvents: function()
17258 if(this.hiddenName){
17260 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17262 this.hiddenField.dom.value =
17263 this.hiddenValue !== undefined ? this.hiddenValue :
17264 this.value !== undefined ? this.value : '';
17266 // prevent input submission
17267 this.el.dom.removeAttribute('name');
17268 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17273 // this.list = this.el.select('ul.dropdown-menu',true).first();
17275 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17276 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17277 if(this.triggerList){
17278 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17281 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17282 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17284 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17285 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17287 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17288 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17290 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17291 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17292 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17295 this.cancelBtn.hide();
17300 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17301 _this.list.setWidth(lw);
17304 this.list.on('mouseover', this.onViewOver, this);
17305 this.list.on('mousemove', this.onViewMove, this);
17307 this.list.on('scroll', this.onViewScroll, this);
17310 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17311 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17314 this.view = new Roo.View(this.list, this.tpl, {
17319 selectedClass: this.selectedClass
17322 //this.view.wrapEl.setDisplayed(false);
17323 this.view.on('click', this.onViewClick, this);
17327 this.store.on('beforeload', this.onBeforeLoad, this);
17328 this.store.on('load', this.onLoad, this);
17329 this.store.on('loadexception', this.onLoadException, this);
17332 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17333 "up" : function(e){
17334 this.inKeyMode = true;
17338 "down" : function(e){
17339 this.inKeyMode = true;
17343 "enter" : function(e){
17344 if(this.fireEvent("specialkey", this, e)){
17345 this.onViewClick(false);
17351 "esc" : function(e){
17352 this.onTickableFooterButtonClick(e, false, false);
17355 "tab" : function(e){
17356 this.fireEvent("specialkey", this, e);
17358 this.onTickableFooterButtonClick(e, false, false);
17365 doRelay : function(e, fn, key){
17366 if(this.scope.isExpanded()){
17367 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17376 this.queryDelay = Math.max(this.queryDelay || 10,
17377 this.mode == 'local' ? 10 : 250);
17380 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17382 if(this.typeAhead){
17383 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17386 if(this.editable !== false){
17387 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17390 this.indicator = this.indicatorEl();
17392 if(this.indicator){
17393 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17394 this.indicator.hide();
17399 onDestroy : function(){
17401 this.view.setStore(null);
17402 this.view.el.removeAllListeners();
17403 this.view.el.remove();
17404 this.view.purgeListeners();
17407 this.list.dom.innerHTML = '';
17411 this.store.un('beforeload', this.onBeforeLoad, this);
17412 this.store.un('load', this.onLoad, this);
17413 this.store.un('loadexception', this.onLoadException, this);
17415 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17419 fireKey : function(e){
17420 if(e.isNavKeyPress() && !this.list.isVisible()){
17421 this.fireEvent("specialkey", this, e);
17426 onResize: function(w, h)
17430 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17432 // if(typeof w != 'number'){
17433 // // we do not handle it!?!?
17436 // var tw = this.trigger.getWidth();
17437 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17438 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17440 // this.inputEl().setWidth( this.adjustWidth('input', x));
17442 // //this.trigger.setStyle('left', x+'px');
17444 // if(this.list && this.listWidth === undefined){
17445 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17446 // this.list.setWidth(lw);
17447 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17455 * Allow or prevent the user from directly editing the field text. If false is passed,
17456 * the user will only be able to select from the items defined in the dropdown list. This method
17457 * is the runtime equivalent of setting the 'editable' config option at config time.
17458 * @param {Boolean} value True to allow the user to directly edit the field text
17460 setEditable : function(value){
17461 if(value == this.editable){
17464 this.editable = value;
17466 this.inputEl().dom.setAttribute('readOnly', true);
17467 this.inputEl().on('mousedown', this.onTriggerClick, this);
17468 this.inputEl().addClass('x-combo-noedit');
17470 this.inputEl().dom.removeAttribute('readOnly');
17471 this.inputEl().un('mousedown', this.onTriggerClick, this);
17472 this.inputEl().removeClass('x-combo-noedit');
17478 onBeforeLoad : function(combo,opts){
17479 if(!this.hasFocus){
17483 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17485 this.restrictHeight();
17486 this.selectedIndex = -1;
17490 onLoad : function(){
17492 this.hasQuery = false;
17494 if(!this.hasFocus){
17498 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17499 this.loading.hide();
17502 if(this.store.getCount() > 0){
17505 this.restrictHeight();
17506 if(this.lastQuery == this.allQuery){
17507 if(this.editable && !this.tickable){
17508 this.inputEl().dom.select();
17512 !this.selectByValue(this.value, true) &&
17515 !this.store.lastOptions ||
17516 typeof(this.store.lastOptions.add) == 'undefined' ||
17517 this.store.lastOptions.add != true
17520 this.select(0, true);
17523 if(this.autoFocus){
17526 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17527 this.taTask.delay(this.typeAheadDelay);
17531 this.onEmptyResults();
17537 onLoadException : function()
17539 this.hasQuery = false;
17541 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17542 this.loading.hide();
17545 if(this.tickable && this.editable){
17550 // only causes errors at present
17551 //Roo.log(this.store.reader.jsonData);
17552 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17554 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17560 onTypeAhead : function(){
17561 if(this.store.getCount() > 0){
17562 var r = this.store.getAt(0);
17563 var newValue = r.data[this.displayField];
17564 var len = newValue.length;
17565 var selStart = this.getRawValue().length;
17567 if(selStart != len){
17568 this.setRawValue(newValue);
17569 this.selectText(selStart, newValue.length);
17575 onSelect : function(record, index){
17577 if(this.fireEvent('beforeselect', this, record, index) !== false){
17579 this.setFromData(index > -1 ? record.data : false);
17582 this.fireEvent('select', this, record, index);
17587 * Returns the currently selected field value or empty string if no value is set.
17588 * @return {String} value The selected value
17590 getValue : function()
17592 if(Roo.isIOS && this.useNativeIOS){
17593 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17597 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17600 if(this.valueField){
17601 return typeof this.value != 'undefined' ? this.value : '';
17603 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17607 getRawValue : function()
17609 if(Roo.isIOS && this.useNativeIOS){
17610 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17613 var v = this.inputEl().getValue();
17619 * Clears any text/value currently set in the field
17621 clearValue : function(){
17623 if(this.hiddenField){
17624 this.hiddenField.dom.value = '';
17627 this.setRawValue('');
17628 this.lastSelectionText = '';
17629 this.lastData = false;
17631 var close = this.closeTriggerEl();
17642 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17643 * will be displayed in the field. If the value does not match the data value of an existing item,
17644 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17645 * Otherwise the field will be blank (although the value will still be set).
17646 * @param {String} value The value to match
17648 setValue : function(v)
17650 if(Roo.isIOS && this.useNativeIOS){
17651 this.setIOSValue(v);
17661 if(this.valueField){
17662 var r = this.findRecord(this.valueField, v);
17664 text = r.data[this.displayField];
17665 }else if(this.valueNotFoundText !== undefined){
17666 text = this.valueNotFoundText;
17669 this.lastSelectionText = text;
17670 if(this.hiddenField){
17671 this.hiddenField.dom.value = v;
17673 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17676 var close = this.closeTriggerEl();
17679 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17685 * @property {Object} the last set data for the element
17690 * Sets the value of the field based on a object which is related to the record format for the store.
17691 * @param {Object} value the value to set as. or false on reset?
17693 setFromData : function(o){
17700 var dv = ''; // display value
17701 var vv = ''; // value value..
17703 if (this.displayField) {
17704 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17706 // this is an error condition!!!
17707 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17710 if(this.valueField){
17711 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17714 var close = this.closeTriggerEl();
17717 if(dv.length || vv * 1 > 0){
17719 this.blockFocus=true;
17725 if(this.hiddenField){
17726 this.hiddenField.dom.value = vv;
17728 this.lastSelectionText = dv;
17729 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17733 // no hidden field.. - we store the value in 'value', but still display
17734 // display field!!!!
17735 this.lastSelectionText = dv;
17736 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17743 reset : function(){
17744 // overridden so that last data is reset..
17751 this.setValue(this.originalValue);
17752 //this.clearInvalid();
17753 this.lastData = false;
17755 this.view.clearSelections();
17761 findRecord : function(prop, value){
17763 if(this.store.getCount() > 0){
17764 this.store.each(function(r){
17765 if(r.data[prop] == value){
17775 getName: function()
17777 // returns hidden if it's set..
17778 if (!this.rendered) {return ''};
17779 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17783 onViewMove : function(e, t){
17784 this.inKeyMode = false;
17788 onViewOver : function(e, t){
17789 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17792 var item = this.view.findItemFromChild(t);
17795 var index = this.view.indexOf(item);
17796 this.select(index, false);
17801 onViewClick : function(view, doFocus, el, e)
17803 var index = this.view.getSelectedIndexes()[0];
17805 var r = this.store.getAt(index);
17809 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17816 Roo.each(this.tickItems, function(v,k){
17818 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17820 _this.tickItems.splice(k, 1);
17822 if(typeof(e) == 'undefined' && view == false){
17823 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17835 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17836 this.tickItems.push(r.data);
17839 if(typeof(e) == 'undefined' && view == false){
17840 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17847 this.onSelect(r, index);
17849 if(doFocus !== false && !this.blockFocus){
17850 this.inputEl().focus();
17855 restrictHeight : function(){
17856 //this.innerList.dom.style.height = '';
17857 //var inner = this.innerList.dom;
17858 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17859 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17860 //this.list.beginUpdate();
17861 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17862 this.list.alignTo(this.inputEl(), this.listAlign);
17863 this.list.alignTo(this.inputEl(), this.listAlign);
17864 //this.list.endUpdate();
17868 onEmptyResults : function(){
17870 if(this.tickable && this.editable){
17871 this.hasFocus = false;
17872 this.restrictHeight();
17880 * Returns true if the dropdown list is expanded, else false.
17882 isExpanded : function(){
17883 return this.list.isVisible();
17887 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17888 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17889 * @param {String} value The data value of the item to select
17890 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17891 * selected item if it is not currently in view (defaults to true)
17892 * @return {Boolean} True if the value matched an item in the list, else false
17894 selectByValue : function(v, scrollIntoView){
17895 if(v !== undefined && v !== null){
17896 var r = this.findRecord(this.valueField || this.displayField, v);
17898 this.select(this.store.indexOf(r), scrollIntoView);
17906 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17907 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17908 * @param {Number} index The zero-based index of the list item to select
17909 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17910 * selected item if it is not currently in view (defaults to true)
17912 select : function(index, scrollIntoView){
17913 this.selectedIndex = index;
17914 this.view.select(index);
17915 if(scrollIntoView !== false){
17916 var el = this.view.getNode(index);
17918 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17921 this.list.scrollChildIntoView(el, false);
17927 selectNext : function(){
17928 var ct = this.store.getCount();
17930 if(this.selectedIndex == -1){
17932 }else if(this.selectedIndex < ct-1){
17933 this.select(this.selectedIndex+1);
17939 selectPrev : function(){
17940 var ct = this.store.getCount();
17942 if(this.selectedIndex == -1){
17944 }else if(this.selectedIndex != 0){
17945 this.select(this.selectedIndex-1);
17951 onKeyUp : function(e){
17952 if(this.editable !== false && !e.isSpecialKey()){
17953 this.lastKey = e.getKey();
17954 this.dqTask.delay(this.queryDelay);
17959 validateBlur : function(){
17960 return !this.list || !this.list.isVisible();
17964 initQuery : function(){
17966 var v = this.getRawValue();
17968 if(this.tickable && this.editable){
17969 v = this.tickableInputEl().getValue();
17976 doForce : function(){
17977 if(this.inputEl().dom.value.length > 0){
17978 this.inputEl().dom.value =
17979 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17985 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17986 * query allowing the query action to be canceled if needed.
17987 * @param {String} query The SQL query to execute
17988 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17989 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17990 * saved in the current store (defaults to false)
17992 doQuery : function(q, forceAll){
17994 if(q === undefined || q === null){
17999 forceAll: forceAll,
18003 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18008 forceAll = qe.forceAll;
18009 if(forceAll === true || (q.length >= this.minChars)){
18011 this.hasQuery = true;
18013 if(this.lastQuery != q || this.alwaysQuery){
18014 this.lastQuery = q;
18015 if(this.mode == 'local'){
18016 this.selectedIndex = -1;
18018 this.store.clearFilter();
18021 if(this.specialFilter){
18022 this.fireEvent('specialfilter', this);
18027 this.store.filter(this.displayField, q);
18030 this.store.fireEvent("datachanged", this.store);
18037 this.store.baseParams[this.queryParam] = q;
18039 var options = {params : this.getParams(q)};
18042 options.add = true;
18043 options.params.start = this.page * this.pageSize;
18046 this.store.load(options);
18049 * this code will make the page width larger, at the beginning, the list not align correctly,
18050 * we should expand the list on onLoad
18051 * so command out it
18056 this.selectedIndex = -1;
18061 this.loadNext = false;
18065 getParams : function(q){
18067 //p[this.queryParam] = q;
18071 p.limit = this.pageSize;
18077 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18079 collapse : function(){
18080 if(!this.isExpanded()){
18086 this.hasFocus = false;
18090 this.cancelBtn.hide();
18091 this.trigger.show();
18094 this.tickableInputEl().dom.value = '';
18095 this.tickableInputEl().blur();
18100 Roo.get(document).un('mousedown', this.collapseIf, this);
18101 Roo.get(document).un('mousewheel', this.collapseIf, this);
18102 if (!this.editable) {
18103 Roo.get(document).un('keydown', this.listKeyPress, this);
18105 this.fireEvent('collapse', this);
18111 collapseIf : function(e){
18112 var in_combo = e.within(this.el);
18113 var in_list = e.within(this.list);
18114 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18116 if (in_combo || in_list || is_list) {
18117 //e.stopPropagation();
18122 this.onTickableFooterButtonClick(e, false, false);
18130 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18132 expand : function(){
18134 if(this.isExpanded() || !this.hasFocus){
18138 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18139 this.list.setWidth(lw);
18145 this.restrictHeight();
18149 this.tickItems = Roo.apply([], this.item);
18152 this.cancelBtn.show();
18153 this.trigger.hide();
18156 this.tickableInputEl().focus();
18161 Roo.get(document).on('mousedown', this.collapseIf, this);
18162 Roo.get(document).on('mousewheel', this.collapseIf, this);
18163 if (!this.editable) {
18164 Roo.get(document).on('keydown', this.listKeyPress, this);
18167 this.fireEvent('expand', this);
18171 // Implements the default empty TriggerField.onTriggerClick function
18172 onTriggerClick : function(e)
18174 Roo.log('trigger click');
18176 if(this.disabled || !this.triggerList){
18181 this.loadNext = false;
18183 if(this.isExpanded()){
18185 if (!this.blockFocus) {
18186 this.inputEl().focus();
18190 this.hasFocus = true;
18191 if(this.triggerAction == 'all') {
18192 this.doQuery(this.allQuery, true);
18194 this.doQuery(this.getRawValue());
18196 if (!this.blockFocus) {
18197 this.inputEl().focus();
18202 onTickableTriggerClick : function(e)
18209 this.loadNext = false;
18210 this.hasFocus = true;
18212 if(this.triggerAction == 'all') {
18213 this.doQuery(this.allQuery, true);
18215 this.doQuery(this.getRawValue());
18219 onSearchFieldClick : function(e)
18221 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18222 this.onTickableFooterButtonClick(e, false, false);
18226 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18231 this.loadNext = false;
18232 this.hasFocus = true;
18234 if(this.triggerAction == 'all') {
18235 this.doQuery(this.allQuery, true);
18237 this.doQuery(this.getRawValue());
18241 listKeyPress : function(e)
18243 //Roo.log('listkeypress');
18244 // scroll to first matching element based on key pres..
18245 if (e.isSpecialKey()) {
18248 var k = String.fromCharCode(e.getKey()).toUpperCase();
18251 var csel = this.view.getSelectedNodes();
18252 var cselitem = false;
18254 var ix = this.view.indexOf(csel[0]);
18255 cselitem = this.store.getAt(ix);
18256 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18262 this.store.each(function(v) {
18264 // start at existing selection.
18265 if (cselitem.id == v.id) {
18271 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18272 match = this.store.indexOf(v);
18278 if (match === false) {
18279 return true; // no more action?
18282 this.view.select(match);
18283 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18284 sn.scrollIntoView(sn.dom.parentNode, false);
18287 onViewScroll : function(e, t){
18289 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){
18293 this.hasQuery = true;
18295 this.loading = this.list.select('.loading', true).first();
18297 if(this.loading === null){
18298 this.list.createChild({
18300 cls: 'loading roo-select2-more-results roo-select2-active',
18301 html: 'Loading more results...'
18304 this.loading = this.list.select('.loading', true).first();
18306 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18308 this.loading.hide();
18311 this.loading.show();
18316 this.loadNext = true;
18318 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18323 addItem : function(o)
18325 var dv = ''; // display value
18327 if (this.displayField) {
18328 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18330 // this is an error condition!!!
18331 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18338 var choice = this.choices.createChild({
18340 cls: 'roo-select2-search-choice',
18349 cls: 'roo-select2-search-choice-close fa fa-times',
18354 }, this.searchField);
18356 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18358 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18366 this.inputEl().dom.value = '';
18371 onRemoveItem : function(e, _self, o)
18373 e.preventDefault();
18375 this.lastItem = Roo.apply([], this.item);
18377 var index = this.item.indexOf(o.data) * 1;
18380 Roo.log('not this item?!');
18384 this.item.splice(index, 1);
18389 this.fireEvent('remove', this, e);
18395 syncValue : function()
18397 if(!this.item.length){
18404 Roo.each(this.item, function(i){
18405 if(_this.valueField){
18406 value.push(i[_this.valueField]);
18413 this.value = value.join(',');
18415 if(this.hiddenField){
18416 this.hiddenField.dom.value = this.value;
18419 this.store.fireEvent("datachanged", this.store);
18424 clearItem : function()
18426 if(!this.multiple){
18432 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18440 if(this.tickable && !Roo.isTouch){
18441 this.view.refresh();
18445 inputEl: function ()
18447 if(Roo.isIOS && this.useNativeIOS){
18448 return this.el.select('select.roo-ios-select', true).first();
18451 if(Roo.isTouch && this.mobileTouchView){
18452 return this.el.select('input.form-control',true).first();
18456 return this.searchField;
18459 return this.el.select('input.form-control',true).first();
18462 onTickableFooterButtonClick : function(e, btn, el)
18464 e.preventDefault();
18466 this.lastItem = Roo.apply([], this.item);
18468 if(btn && btn.name == 'cancel'){
18469 this.tickItems = Roo.apply([], this.item);
18478 Roo.each(this.tickItems, function(o){
18486 validate : function()
18488 if(this.getVisibilityEl().hasClass('hidden')){
18492 var v = this.getRawValue();
18495 v = this.getValue();
18498 if(this.disabled || this.allowBlank || v.length){
18503 this.markInvalid();
18507 tickableInputEl : function()
18509 if(!this.tickable || !this.editable){
18510 return this.inputEl();
18513 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18517 getAutoCreateTouchView : function()
18522 cls: 'form-group' //input-group
18528 type : this.inputType,
18529 cls : 'form-control x-combo-noedit',
18530 autocomplete: 'new-password',
18531 placeholder : this.placeholder || '',
18536 input.name = this.name;
18540 input.cls += ' input-' + this.size;
18543 if (this.disabled) {
18544 input.disabled = true;
18548 cls : 'roo-combobox-wrap',
18555 inputblock.cls += ' input-group';
18557 inputblock.cn.unshift({
18559 cls : 'input-group-addon input-group-prepend input-group-text',
18564 if(this.removable && !this.multiple){
18565 inputblock.cls += ' roo-removable';
18567 inputblock.cn.push({
18570 cls : 'roo-combo-removable-btn close'
18574 if(this.hasFeedback && !this.allowBlank){
18576 inputblock.cls += ' has-feedback';
18578 inputblock.cn.push({
18580 cls: 'glyphicon form-control-feedback'
18587 inputblock.cls += (this.before) ? '' : ' input-group';
18589 inputblock.cn.push({
18591 cls : 'input-group-addon input-group-append input-group-text',
18597 var ibwrap = inputblock;
18602 cls: 'roo-select2-choices',
18606 cls: 'roo-select2-search-field',
18619 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18624 cls: 'form-hidden-field'
18630 if(!this.multiple && this.showToggleBtn){
18636 if (this.caret != false) {
18639 cls: 'fa fa-' + this.caret
18646 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18648 Roo.bootstrap.version == 3 ? caret : '',
18651 cls: 'combobox-clear',
18665 combobox.cls += ' roo-select2-container-multi';
18668 var required = this.allowBlank ? {
18670 style: 'display: none'
18673 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18674 tooltip : 'This field is required'
18677 var align = this.labelAlign || this.parentLabelAlign();
18679 if (align ==='left' && this.fieldLabel.length) {
18685 cls : 'control-label col-form-label',
18686 html : this.fieldLabel
18690 cls : 'roo-combobox-wrap ',
18697 var labelCfg = cfg.cn[1];
18698 var contentCfg = cfg.cn[2];
18701 if(this.indicatorpos == 'right'){
18706 cls : 'control-label col-form-label',
18710 html : this.fieldLabel
18716 cls : "roo-combobox-wrap ",
18724 labelCfg = cfg.cn[0];
18725 contentCfg = cfg.cn[1];
18730 if(this.labelWidth > 12){
18731 labelCfg.style = "width: " + this.labelWidth + 'px';
18734 if(this.labelWidth < 13 && this.labelmd == 0){
18735 this.labelmd = this.labelWidth;
18738 if(this.labellg > 0){
18739 labelCfg.cls += ' col-lg-' + this.labellg;
18740 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18743 if(this.labelmd > 0){
18744 labelCfg.cls += ' col-md-' + this.labelmd;
18745 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18748 if(this.labelsm > 0){
18749 labelCfg.cls += ' col-sm-' + this.labelsm;
18750 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18753 if(this.labelxs > 0){
18754 labelCfg.cls += ' col-xs-' + this.labelxs;
18755 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18759 } else if ( this.fieldLabel.length) {
18764 cls : 'control-label',
18765 html : this.fieldLabel
18776 if(this.indicatorpos == 'right'){
18780 cls : 'control-label',
18781 html : this.fieldLabel,
18799 var settings = this;
18801 ['xs','sm','md','lg'].map(function(size){
18802 if (settings[size]) {
18803 cfg.cls += ' col-' + size + '-' + settings[size];
18810 initTouchView : function()
18812 this.renderTouchView();
18814 this.touchViewEl.on('scroll', function(){
18815 this.el.dom.scrollTop = 0;
18818 this.originalValue = this.getValue();
18820 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18822 this.inputEl().on("click", this.showTouchView, this);
18823 if (this.triggerEl) {
18824 this.triggerEl.on("click", this.showTouchView, this);
18828 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18829 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18831 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18833 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18834 this.store.on('load', this.onTouchViewLoad, this);
18835 this.store.on('loadexception', this.onTouchViewLoadException, this);
18837 if(this.hiddenName){
18839 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18841 this.hiddenField.dom.value =
18842 this.hiddenValue !== undefined ? this.hiddenValue :
18843 this.value !== undefined ? this.value : '';
18845 this.el.dom.removeAttribute('name');
18846 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18850 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18851 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18854 if(this.removable && !this.multiple){
18855 var close = this.closeTriggerEl();
18857 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18858 close.on('click', this.removeBtnClick, this, close);
18862 * fix the bug in Safari iOS8
18864 this.inputEl().on("focus", function(e){
18865 document.activeElement.blur();
18868 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18875 renderTouchView : function()
18877 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18878 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18880 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18881 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18883 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18884 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18885 this.touchViewBodyEl.setStyle('overflow', 'auto');
18887 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18888 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18891 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18895 showTouchView : function()
18901 this.touchViewHeaderEl.hide();
18903 if(this.modalTitle.length){
18904 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18905 this.touchViewHeaderEl.show();
18908 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18909 this.touchViewEl.show();
18911 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18913 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18914 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18916 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18918 if(this.modalTitle.length){
18919 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18922 this.touchViewBodyEl.setHeight(bodyHeight);
18926 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18928 this.touchViewEl.addClass(['in','show']);
18931 if(this._touchViewMask){
18932 Roo.get(document.body).addClass("x-body-masked");
18933 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18934 this._touchViewMask.setStyle('z-index', 10000);
18935 this._touchViewMask.addClass('show');
18938 this.doTouchViewQuery();
18942 hideTouchView : function()
18944 this.touchViewEl.removeClass(['in','show']);
18948 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18950 this.touchViewEl.setStyle('display', 'none');
18953 if(this._touchViewMask){
18954 this._touchViewMask.removeClass('show');
18955 Roo.get(document.body).removeClass("x-body-masked");
18959 setTouchViewValue : function()
18966 Roo.each(this.tickItems, function(o){
18971 this.hideTouchView();
18974 doTouchViewQuery : function()
18983 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18987 if(!this.alwaysQuery || this.mode == 'local'){
18988 this.onTouchViewLoad();
18995 onTouchViewBeforeLoad : function(combo,opts)
19001 onTouchViewLoad : function()
19003 if(this.store.getCount() < 1){
19004 this.onTouchViewEmptyResults();
19008 this.clearTouchView();
19010 var rawValue = this.getRawValue();
19012 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19014 this.tickItems = [];
19016 this.store.data.each(function(d, rowIndex){
19017 var row = this.touchViewListGroup.createChild(template);
19019 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19020 row.addClass(d.data.cls);
19023 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19026 html : d.data[this.displayField]
19029 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19030 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19033 row.removeClass('selected');
19034 if(!this.multiple && this.valueField &&
19035 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19038 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19039 row.addClass('selected');
19042 if(this.multiple && this.valueField &&
19043 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19047 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19048 this.tickItems.push(d.data);
19051 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19055 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19057 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19059 if(this.modalTitle.length){
19060 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19063 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19065 if(this.mobile_restrict_height && listHeight < bodyHeight){
19066 this.touchViewBodyEl.setHeight(listHeight);
19071 if(firstChecked && listHeight > bodyHeight){
19072 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19077 onTouchViewLoadException : function()
19079 this.hideTouchView();
19082 onTouchViewEmptyResults : function()
19084 this.clearTouchView();
19086 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19088 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19092 clearTouchView : function()
19094 this.touchViewListGroup.dom.innerHTML = '';
19097 onTouchViewClick : function(e, el, o)
19099 e.preventDefault();
19102 var rowIndex = o.rowIndex;
19104 var r = this.store.getAt(rowIndex);
19106 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19108 if(!this.multiple){
19109 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19110 c.dom.removeAttribute('checked');
19113 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19115 this.setFromData(r.data);
19117 var close = this.closeTriggerEl();
19123 this.hideTouchView();
19125 this.fireEvent('select', this, r, rowIndex);
19130 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19131 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19132 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19136 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19137 this.addItem(r.data);
19138 this.tickItems.push(r.data);
19142 getAutoCreateNativeIOS : function()
19145 cls: 'form-group' //input-group,
19150 cls : 'roo-ios-select'
19154 combobox.name = this.name;
19157 if (this.disabled) {
19158 combobox.disabled = true;
19161 var settings = this;
19163 ['xs','sm','md','lg'].map(function(size){
19164 if (settings[size]) {
19165 cfg.cls += ' col-' + size + '-' + settings[size];
19175 initIOSView : function()
19177 this.store.on('load', this.onIOSViewLoad, this);
19182 onIOSViewLoad : function()
19184 if(this.store.getCount() < 1){
19188 this.clearIOSView();
19190 if(this.allowBlank) {
19192 var default_text = '-- SELECT --';
19194 if(this.placeholder.length){
19195 default_text = this.placeholder;
19198 if(this.emptyTitle.length){
19199 default_text += ' - ' + this.emptyTitle + ' -';
19202 var opt = this.inputEl().createChild({
19205 html : default_text
19209 o[this.valueField] = 0;
19210 o[this.displayField] = default_text;
19212 this.ios_options.push({
19219 this.store.data.each(function(d, rowIndex){
19223 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19224 html = d.data[this.displayField];
19229 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19230 value = d.data[this.valueField];
19239 if(this.value == d.data[this.valueField]){
19240 option['selected'] = true;
19243 var opt = this.inputEl().createChild(option);
19245 this.ios_options.push({
19252 this.inputEl().on('change', function(){
19253 this.fireEvent('select', this);
19258 clearIOSView: function()
19260 this.inputEl().dom.innerHTML = '';
19262 this.ios_options = [];
19265 setIOSValue: function(v)
19269 if(!this.ios_options){
19273 Roo.each(this.ios_options, function(opts){
19275 opts.el.dom.removeAttribute('selected');
19277 if(opts.data[this.valueField] != v){
19281 opts.el.dom.setAttribute('selected', true);
19287 * @cfg {Boolean} grow
19291 * @cfg {Number} growMin
19295 * @cfg {Number} growMax
19304 Roo.apply(Roo.bootstrap.ComboBox, {
19308 cls: 'modal-header',
19330 cls: 'list-group-item',
19334 cls: 'roo-combobox-list-group-item-value'
19338 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19352 listItemCheckbox : {
19354 cls: 'list-group-item',
19358 cls: 'roo-combobox-list-group-item-value'
19362 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19378 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19383 cls: 'modal-footer',
19391 cls: 'col-xs-6 text-left',
19394 cls: 'btn btn-danger roo-touch-view-cancel',
19400 cls: 'col-xs-6 text-right',
19403 cls: 'btn btn-success roo-touch-view-ok',
19414 Roo.apply(Roo.bootstrap.ComboBox, {
19416 touchViewTemplate : {
19418 cls: 'modal fade roo-combobox-touch-view',
19422 cls: 'modal-dialog',
19423 style : 'position:fixed', // we have to fix position....
19427 cls: 'modal-content',
19429 Roo.bootstrap.ComboBox.header,
19430 Roo.bootstrap.ComboBox.body,
19431 Roo.bootstrap.ComboBox.footer
19440 * Ext JS Library 1.1.1
19441 * Copyright(c) 2006-2007, Ext JS, LLC.
19443 * Originally Released Under LGPL - original licence link has changed is not relivant.
19446 * <script type="text/javascript">
19451 * @extends Roo.util.Observable
19452 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19453 * This class also supports single and multi selection modes. <br>
19454 * Create a data model bound view:
19456 var store = new Roo.data.Store(...);
19458 var view = new Roo.View({
19460 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19462 singleSelect: true,
19463 selectedClass: "ydataview-selected",
19467 // listen for node click?
19468 view.on("click", function(vw, index, node, e){
19469 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19473 dataModel.load("foobar.xml");
19475 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19477 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19478 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19480 * Note: old style constructor is still suported (container, template, config)
19483 * Create a new View
19484 * @param {Object} config The config object
19487 Roo.View = function(config, depreciated_tpl, depreciated_config){
19489 this.parent = false;
19491 if (typeof(depreciated_tpl) == 'undefined') {
19492 // new way.. - universal constructor.
19493 Roo.apply(this, config);
19494 this.el = Roo.get(this.el);
19497 this.el = Roo.get(config);
19498 this.tpl = depreciated_tpl;
19499 Roo.apply(this, depreciated_config);
19501 this.wrapEl = this.el.wrap().wrap();
19502 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19505 if(typeof(this.tpl) == "string"){
19506 this.tpl = new Roo.Template(this.tpl);
19508 // support xtype ctors..
19509 this.tpl = new Roo.factory(this.tpl, Roo);
19513 this.tpl.compile();
19518 * @event beforeclick
19519 * Fires before a click is processed. Returns false to cancel the default action.
19520 * @param {Roo.View} this
19521 * @param {Number} index The index of the target node
19522 * @param {HTMLElement} node The target node
19523 * @param {Roo.EventObject} e The raw event object
19525 "beforeclick" : true,
19528 * Fires when a template node is clicked.
19529 * @param {Roo.View} this
19530 * @param {Number} index The index of the target node
19531 * @param {HTMLElement} node The target node
19532 * @param {Roo.EventObject} e The raw event object
19537 * Fires when a template node is double clicked.
19538 * @param {Roo.View} this
19539 * @param {Number} index The index of the target node
19540 * @param {HTMLElement} node The target node
19541 * @param {Roo.EventObject} e The raw event object
19545 * @event contextmenu
19546 * Fires when a template node is right clicked.
19547 * @param {Roo.View} this
19548 * @param {Number} index The index of the target node
19549 * @param {HTMLElement} node The target node
19550 * @param {Roo.EventObject} e The raw event object
19552 "contextmenu" : true,
19554 * @event selectionchange
19555 * Fires when the selected nodes change.
19556 * @param {Roo.View} this
19557 * @param {Array} selections Array of the selected nodes
19559 "selectionchange" : true,
19562 * @event beforeselect
19563 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19564 * @param {Roo.View} this
19565 * @param {HTMLElement} node The node to be selected
19566 * @param {Array} selections Array of currently selected nodes
19568 "beforeselect" : true,
19570 * @event preparedata
19571 * Fires on every row to render, to allow you to change the data.
19572 * @param {Roo.View} this
19573 * @param {Object} data to be rendered (change this)
19575 "preparedata" : true
19583 "click": this.onClick,
19584 "dblclick": this.onDblClick,
19585 "contextmenu": this.onContextMenu,
19589 this.selections = [];
19591 this.cmp = new Roo.CompositeElementLite([]);
19593 this.store = Roo.factory(this.store, Roo.data);
19594 this.setStore(this.store, true);
19597 if ( this.footer && this.footer.xtype) {
19599 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19601 this.footer.dataSource = this.store;
19602 this.footer.container = fctr;
19603 this.footer = Roo.factory(this.footer, Roo);
19604 fctr.insertFirst(this.el);
19606 // this is a bit insane - as the paging toolbar seems to detach the el..
19607 // dom.parentNode.parentNode.parentNode
19608 // they get detached?
19612 Roo.View.superclass.constructor.call(this);
19617 Roo.extend(Roo.View, Roo.util.Observable, {
19620 * @cfg {Roo.data.Store} store Data store to load data from.
19625 * @cfg {String|Roo.Element} el The container element.
19630 * @cfg {String|Roo.Template} tpl The template used by this View
19634 * @cfg {String} dataName the named area of the template to use as the data area
19635 * Works with domtemplates roo-name="name"
19639 * @cfg {String} selectedClass The css class to add to selected nodes
19641 selectedClass : "x-view-selected",
19643 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19648 * @cfg {String} text to display on mask (default Loading)
19652 * @cfg {Boolean} multiSelect Allow multiple selection
19654 multiSelect : false,
19656 * @cfg {Boolean} singleSelect Allow single selection
19658 singleSelect: false,
19661 * @cfg {Boolean} toggleSelect - selecting
19663 toggleSelect : false,
19666 * @cfg {Boolean} tickable - selecting
19671 * Returns the element this view is bound to.
19672 * @return {Roo.Element}
19674 getEl : function(){
19675 return this.wrapEl;
19681 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19683 refresh : function(){
19684 //Roo.log('refresh');
19687 // if we are using something like 'domtemplate', then
19688 // the what gets used is:
19689 // t.applySubtemplate(NAME, data, wrapping data..)
19690 // the outer template then get' applied with
19691 // the store 'extra data'
19692 // and the body get's added to the
19693 // roo-name="data" node?
19694 // <span class='roo-tpl-{name}'></span> ?????
19698 this.clearSelections();
19699 this.el.update("");
19701 var records = this.store.getRange();
19702 if(records.length < 1) {
19704 // is this valid?? = should it render a template??
19706 this.el.update(this.emptyText);
19710 if (this.dataName) {
19711 this.el.update(t.apply(this.store.meta)); //????
19712 el = this.el.child('.roo-tpl-' + this.dataName);
19715 for(var i = 0, len = records.length; i < len; i++){
19716 var data = this.prepareData(records[i].data, i, records[i]);
19717 this.fireEvent("preparedata", this, data, i, records[i]);
19719 var d = Roo.apply({}, data);
19722 Roo.apply(d, {'roo-id' : Roo.id()});
19726 Roo.each(this.parent.item, function(item){
19727 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19730 Roo.apply(d, {'roo-data-checked' : 'checked'});
19734 html[html.length] = Roo.util.Format.trim(
19736 t.applySubtemplate(this.dataName, d, this.store.meta) :
19743 el.update(html.join(""));
19744 this.nodes = el.dom.childNodes;
19745 this.updateIndexes(0);
19750 * Function to override to reformat the data that is sent to
19751 * the template for each node.
19752 * DEPRICATED - use the preparedata event handler.
19753 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19754 * a JSON object for an UpdateManager bound view).
19756 prepareData : function(data, index, record)
19758 this.fireEvent("preparedata", this, data, index, record);
19762 onUpdate : function(ds, record){
19763 // Roo.log('on update');
19764 this.clearSelections();
19765 var index = this.store.indexOf(record);
19766 var n = this.nodes[index];
19767 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19768 n.parentNode.removeChild(n);
19769 this.updateIndexes(index, index);
19775 onAdd : function(ds, records, index)
19777 //Roo.log(['on Add', ds, records, index] );
19778 this.clearSelections();
19779 if(this.nodes.length == 0){
19783 var n = this.nodes[index];
19784 for(var i = 0, len = records.length; i < len; i++){
19785 var d = this.prepareData(records[i].data, i, records[i]);
19787 this.tpl.insertBefore(n, d);
19790 this.tpl.append(this.el, d);
19793 this.updateIndexes(index);
19796 onRemove : function(ds, record, index){
19797 // Roo.log('onRemove');
19798 this.clearSelections();
19799 var el = this.dataName ?
19800 this.el.child('.roo-tpl-' + this.dataName) :
19803 el.dom.removeChild(this.nodes[index]);
19804 this.updateIndexes(index);
19808 * Refresh an individual node.
19809 * @param {Number} index
19811 refreshNode : function(index){
19812 this.onUpdate(this.store, this.store.getAt(index));
19815 updateIndexes : function(startIndex, endIndex){
19816 var ns = this.nodes;
19817 startIndex = startIndex || 0;
19818 endIndex = endIndex || ns.length - 1;
19819 for(var i = startIndex; i <= endIndex; i++){
19820 ns[i].nodeIndex = i;
19825 * Changes the data store this view uses and refresh the view.
19826 * @param {Store} store
19828 setStore : function(store, initial){
19829 if(!initial && this.store){
19830 this.store.un("datachanged", this.refresh);
19831 this.store.un("add", this.onAdd);
19832 this.store.un("remove", this.onRemove);
19833 this.store.un("update", this.onUpdate);
19834 this.store.un("clear", this.refresh);
19835 this.store.un("beforeload", this.onBeforeLoad);
19836 this.store.un("load", this.onLoad);
19837 this.store.un("loadexception", this.onLoad);
19841 store.on("datachanged", this.refresh, this);
19842 store.on("add", this.onAdd, this);
19843 store.on("remove", this.onRemove, this);
19844 store.on("update", this.onUpdate, this);
19845 store.on("clear", this.refresh, this);
19846 store.on("beforeload", this.onBeforeLoad, this);
19847 store.on("load", this.onLoad, this);
19848 store.on("loadexception", this.onLoad, this);
19856 * onbeforeLoad - masks the loading area.
19859 onBeforeLoad : function(store,opts)
19861 //Roo.log('onBeforeLoad');
19863 this.el.update("");
19865 this.el.mask(this.mask ? this.mask : "Loading" );
19867 onLoad : function ()
19874 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19875 * @param {HTMLElement} node
19876 * @return {HTMLElement} The template node
19878 findItemFromChild : function(node){
19879 var el = this.dataName ?
19880 this.el.child('.roo-tpl-' + this.dataName,true) :
19883 if(!node || node.parentNode == el){
19886 var p = node.parentNode;
19887 while(p && p != el){
19888 if(p.parentNode == el){
19897 onClick : function(e){
19898 var item = this.findItemFromChild(e.getTarget());
19900 var index = this.indexOf(item);
19901 if(this.onItemClick(item, index, e) !== false){
19902 this.fireEvent("click", this, index, item, e);
19905 this.clearSelections();
19910 onContextMenu : function(e){
19911 var item = this.findItemFromChild(e.getTarget());
19913 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19918 onDblClick : function(e){
19919 var item = this.findItemFromChild(e.getTarget());
19921 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19925 onItemClick : function(item, index, e)
19927 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19930 if (this.toggleSelect) {
19931 var m = this.isSelected(item) ? 'unselect' : 'select';
19934 _t[m](item, true, false);
19937 if(this.multiSelect || this.singleSelect){
19938 if(this.multiSelect && e.shiftKey && this.lastSelection){
19939 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19941 this.select(item, this.multiSelect && e.ctrlKey);
19942 this.lastSelection = item;
19945 if(!this.tickable){
19946 e.preventDefault();
19954 * Get the number of selected nodes.
19957 getSelectionCount : function(){
19958 return this.selections.length;
19962 * Get the currently selected nodes.
19963 * @return {Array} An array of HTMLElements
19965 getSelectedNodes : function(){
19966 return this.selections;
19970 * Get the indexes of the selected nodes.
19973 getSelectedIndexes : function(){
19974 var indexes = [], s = this.selections;
19975 for(var i = 0, len = s.length; i < len; i++){
19976 indexes.push(s[i].nodeIndex);
19982 * Clear all selections
19983 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19985 clearSelections : function(suppressEvent){
19986 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19987 this.cmp.elements = this.selections;
19988 this.cmp.removeClass(this.selectedClass);
19989 this.selections = [];
19990 if(!suppressEvent){
19991 this.fireEvent("selectionchange", this, this.selections);
19997 * Returns true if the passed node is selected
19998 * @param {HTMLElement/Number} node The node or node index
19999 * @return {Boolean}
20001 isSelected : function(node){
20002 var s = this.selections;
20006 node = this.getNode(node);
20007 return s.indexOf(node) !== -1;
20012 * @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
20013 * @param {Boolean} keepExisting (optional) true to keep existing selections
20014 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20016 select : function(nodeInfo, keepExisting, suppressEvent){
20017 if(nodeInfo instanceof Array){
20019 this.clearSelections(true);
20021 for(var i = 0, len = nodeInfo.length; i < len; i++){
20022 this.select(nodeInfo[i], true, true);
20026 var node = this.getNode(nodeInfo);
20027 if(!node || this.isSelected(node)){
20028 return; // already selected.
20031 this.clearSelections(true);
20034 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20035 Roo.fly(node).addClass(this.selectedClass);
20036 this.selections.push(node);
20037 if(!suppressEvent){
20038 this.fireEvent("selectionchange", this, this.selections);
20046 * @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
20047 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20048 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20050 unselect : function(nodeInfo, keepExisting, suppressEvent)
20052 if(nodeInfo instanceof Array){
20053 Roo.each(this.selections, function(s) {
20054 this.unselect(s, nodeInfo);
20058 var node = this.getNode(nodeInfo);
20059 if(!node || !this.isSelected(node)){
20060 //Roo.log("not selected");
20061 return; // not selected.
20065 Roo.each(this.selections, function(s) {
20067 Roo.fly(node).removeClass(this.selectedClass);
20074 this.selections= ns;
20075 this.fireEvent("selectionchange", this, this.selections);
20079 * Gets a template node.
20080 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20081 * @return {HTMLElement} The node or null if it wasn't found
20083 getNode : function(nodeInfo){
20084 if(typeof nodeInfo == "string"){
20085 return document.getElementById(nodeInfo);
20086 }else if(typeof nodeInfo == "number"){
20087 return this.nodes[nodeInfo];
20093 * Gets a range template nodes.
20094 * @param {Number} startIndex
20095 * @param {Number} endIndex
20096 * @return {Array} An array of nodes
20098 getNodes : function(start, end){
20099 var ns = this.nodes;
20100 start = start || 0;
20101 end = typeof end == "undefined" ? ns.length - 1 : end;
20104 for(var i = start; i <= end; i++){
20108 for(var i = start; i >= end; i--){
20116 * Finds the index of the passed node
20117 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20118 * @return {Number} The index of the node or -1
20120 indexOf : function(node){
20121 node = this.getNode(node);
20122 if(typeof node.nodeIndex == "number"){
20123 return node.nodeIndex;
20125 var ns = this.nodes;
20126 for(var i = 0, len = ns.length; i < len; i++){
20137 * based on jquery fullcalendar
20141 Roo.bootstrap = Roo.bootstrap || {};
20143 * @class Roo.bootstrap.Calendar
20144 * @extends Roo.bootstrap.Component
20145 * Bootstrap Calendar class
20146 * @cfg {Boolean} loadMask (true|false) default false
20147 * @cfg {Object} header generate the user specific header of the calendar, default false
20150 * Create a new Container
20151 * @param {Object} config The config object
20156 Roo.bootstrap.Calendar = function(config){
20157 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20161 * Fires when a date is selected
20162 * @param {DatePicker} this
20163 * @param {Date} date The selected date
20167 * @event monthchange
20168 * Fires when the displayed month changes
20169 * @param {DatePicker} this
20170 * @param {Date} date The selected month
20172 'monthchange': true,
20174 * @event evententer
20175 * Fires when mouse over an event
20176 * @param {Calendar} this
20177 * @param {event} Event
20179 'evententer': true,
20181 * @event eventleave
20182 * Fires when the mouse leaves an
20183 * @param {Calendar} this
20186 'eventleave': true,
20188 * @event eventclick
20189 * Fires when the mouse click an
20190 * @param {Calendar} this
20199 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20202 * @cfg {Number} startDay
20203 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20211 getAutoCreate : function(){
20214 var fc_button = function(name, corner, style, content ) {
20215 return Roo.apply({},{
20217 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20219 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20222 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20233 style : 'width:100%',
20240 cls : 'fc-header-left',
20242 fc_button('prev', 'left', 'arrow', '‹' ),
20243 fc_button('next', 'right', 'arrow', '›' ),
20244 { tag: 'span', cls: 'fc-header-space' },
20245 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20253 cls : 'fc-header-center',
20257 cls: 'fc-header-title',
20260 html : 'month / year'
20268 cls : 'fc-header-right',
20270 /* fc_button('month', 'left', '', 'month' ),
20271 fc_button('week', '', '', 'week' ),
20272 fc_button('day', 'right', '', 'day' )
20284 header = this.header;
20287 var cal_heads = function() {
20289 // fixme - handle this.
20291 for (var i =0; i < Date.dayNames.length; i++) {
20292 var d = Date.dayNames[i];
20295 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20296 html : d.substring(0,3)
20300 ret[0].cls += ' fc-first';
20301 ret[6].cls += ' fc-last';
20304 var cal_cell = function(n) {
20307 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20312 cls: 'fc-day-number',
20316 cls: 'fc-day-content',
20320 style: 'position: relative;' // height: 17px;
20332 var cal_rows = function() {
20335 for (var r = 0; r < 6; r++) {
20342 for (var i =0; i < Date.dayNames.length; i++) {
20343 var d = Date.dayNames[i];
20344 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20347 row.cn[0].cls+=' fc-first';
20348 row.cn[0].cn[0].style = 'min-height:90px';
20349 row.cn[6].cls+=' fc-last';
20353 ret[0].cls += ' fc-first';
20354 ret[4].cls += ' fc-prev-last';
20355 ret[5].cls += ' fc-last';
20362 cls: 'fc-border-separate',
20363 style : 'width:100%',
20371 cls : 'fc-first fc-last',
20389 cls : 'fc-content',
20390 style : "position: relative;",
20393 cls : 'fc-view fc-view-month fc-grid',
20394 style : 'position: relative',
20395 unselectable : 'on',
20398 cls : 'fc-event-container',
20399 style : 'position:absolute;z-index:8;top:0;left:0;'
20417 initEvents : function()
20420 throw "can not find store for calendar";
20426 style: "text-align:center",
20430 style: "background-color:white;width:50%;margin:250 auto",
20434 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20445 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20447 var size = this.el.select('.fc-content', true).first().getSize();
20448 this.maskEl.setSize(size.width, size.height);
20449 this.maskEl.enableDisplayMode("block");
20450 if(!this.loadMask){
20451 this.maskEl.hide();
20454 this.store = Roo.factory(this.store, Roo.data);
20455 this.store.on('load', this.onLoad, this);
20456 this.store.on('beforeload', this.onBeforeLoad, this);
20460 this.cells = this.el.select('.fc-day',true);
20461 //Roo.log(this.cells);
20462 this.textNodes = this.el.query('.fc-day-number');
20463 this.cells.addClassOnOver('fc-state-hover');
20465 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20466 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20467 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20468 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20470 this.on('monthchange', this.onMonthChange, this);
20472 this.update(new Date().clearTime());
20475 resize : function() {
20476 var sz = this.el.getSize();
20478 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20479 this.el.select('.fc-day-content div',true).setHeight(34);
20484 showPrevMonth : function(e){
20485 this.update(this.activeDate.add("mo", -1));
20487 showToday : function(e){
20488 this.update(new Date().clearTime());
20491 showNextMonth : function(e){
20492 this.update(this.activeDate.add("mo", 1));
20496 showPrevYear : function(){
20497 this.update(this.activeDate.add("y", -1));
20501 showNextYear : function(){
20502 this.update(this.activeDate.add("y", 1));
20507 update : function(date)
20509 var vd = this.activeDate;
20510 this.activeDate = date;
20511 // if(vd && this.el){
20512 // var t = date.getTime();
20513 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20514 // Roo.log('using add remove');
20516 // this.fireEvent('monthchange', this, date);
20518 // this.cells.removeClass("fc-state-highlight");
20519 // this.cells.each(function(c){
20520 // if(c.dateValue == t){
20521 // c.addClass("fc-state-highlight");
20522 // setTimeout(function(){
20523 // try{c.dom.firstChild.focus();}catch(e){}
20533 var days = date.getDaysInMonth();
20535 var firstOfMonth = date.getFirstDateOfMonth();
20536 var startingPos = firstOfMonth.getDay()-this.startDay;
20538 if(startingPos < this.startDay){
20542 var pm = date.add(Date.MONTH, -1);
20543 var prevStart = pm.getDaysInMonth()-startingPos;
20545 this.cells = this.el.select('.fc-day',true);
20546 this.textNodes = this.el.query('.fc-day-number');
20547 this.cells.addClassOnOver('fc-state-hover');
20549 var cells = this.cells.elements;
20550 var textEls = this.textNodes;
20552 Roo.each(cells, function(cell){
20553 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20556 days += startingPos;
20558 // convert everything to numbers so it's fast
20559 var day = 86400000;
20560 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20563 //Roo.log(prevStart);
20565 var today = new Date().clearTime().getTime();
20566 var sel = date.clearTime().getTime();
20567 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20568 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20569 var ddMatch = this.disabledDatesRE;
20570 var ddText = this.disabledDatesText;
20571 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20572 var ddaysText = this.disabledDaysText;
20573 var format = this.format;
20575 var setCellClass = function(cal, cell){
20579 //Roo.log('set Cell Class');
20581 var t = d.getTime();
20585 cell.dateValue = t;
20587 cell.className += " fc-today";
20588 cell.className += " fc-state-highlight";
20589 cell.title = cal.todayText;
20592 // disable highlight in other month..
20593 //cell.className += " fc-state-highlight";
20598 cell.className = " fc-state-disabled";
20599 cell.title = cal.minText;
20603 cell.className = " fc-state-disabled";
20604 cell.title = cal.maxText;
20608 if(ddays.indexOf(d.getDay()) != -1){
20609 cell.title = ddaysText;
20610 cell.className = " fc-state-disabled";
20613 if(ddMatch && format){
20614 var fvalue = d.dateFormat(format);
20615 if(ddMatch.test(fvalue)){
20616 cell.title = ddText.replace("%0", fvalue);
20617 cell.className = " fc-state-disabled";
20621 if (!cell.initialClassName) {
20622 cell.initialClassName = cell.dom.className;
20625 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20630 for(; i < startingPos; i++) {
20631 textEls[i].innerHTML = (++prevStart);
20632 d.setDate(d.getDate()+1);
20634 cells[i].className = "fc-past fc-other-month";
20635 setCellClass(this, cells[i]);
20640 for(; i < days; i++){
20641 intDay = i - startingPos + 1;
20642 textEls[i].innerHTML = (intDay);
20643 d.setDate(d.getDate()+1);
20645 cells[i].className = ''; // "x-date-active";
20646 setCellClass(this, cells[i]);
20650 for(; i < 42; i++) {
20651 textEls[i].innerHTML = (++extraDays);
20652 d.setDate(d.getDate()+1);
20654 cells[i].className = "fc-future fc-other-month";
20655 setCellClass(this, cells[i]);
20658 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20660 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20662 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20663 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20665 if(totalRows != 6){
20666 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20667 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20670 this.fireEvent('monthchange', this, date);
20674 if(!this.internalRender){
20675 var main = this.el.dom.firstChild;
20676 var w = main.offsetWidth;
20677 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20678 Roo.fly(main).setWidth(w);
20679 this.internalRender = true;
20680 // opera does not respect the auto grow header center column
20681 // then, after it gets a width opera refuses to recalculate
20682 // without a second pass
20683 if(Roo.isOpera && !this.secondPass){
20684 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20685 this.secondPass = true;
20686 this.update.defer(10, this, [date]);
20693 findCell : function(dt) {
20694 dt = dt.clearTime().getTime();
20696 this.cells.each(function(c){
20697 //Roo.log("check " +c.dateValue + '?=' + dt);
20698 if(c.dateValue == dt){
20708 findCells : function(ev) {
20709 var s = ev.start.clone().clearTime().getTime();
20711 var e= ev.end.clone().clearTime().getTime();
20714 this.cells.each(function(c){
20715 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20717 if(c.dateValue > e){
20720 if(c.dateValue < s){
20729 // findBestRow: function(cells)
20733 // for (var i =0 ; i < cells.length;i++) {
20734 // ret = Math.max(cells[i].rows || 0,ret);
20741 addItem : function(ev)
20743 // look for vertical location slot in
20744 var cells = this.findCells(ev);
20746 // ev.row = this.findBestRow(cells);
20748 // work out the location.
20752 for(var i =0; i < cells.length; i++) {
20754 cells[i].row = cells[0].row;
20757 cells[i].row = cells[i].row + 1;
20767 if (crow.start.getY() == cells[i].getY()) {
20769 crow.end = cells[i];
20786 cells[0].events.push(ev);
20788 this.calevents.push(ev);
20791 clearEvents: function() {
20793 if(!this.calevents){
20797 Roo.each(this.cells.elements, function(c){
20803 Roo.each(this.calevents, function(e) {
20804 Roo.each(e.els, function(el) {
20805 el.un('mouseenter' ,this.onEventEnter, this);
20806 el.un('mouseleave' ,this.onEventLeave, this);
20811 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20817 renderEvents: function()
20821 this.cells.each(function(c) {
20830 if(c.row != c.events.length){
20831 r = 4 - (4 - (c.row - c.events.length));
20834 c.events = ev.slice(0, r);
20835 c.more = ev.slice(r);
20837 if(c.more.length && c.more.length == 1){
20838 c.events.push(c.more.pop());
20841 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20845 this.cells.each(function(c) {
20847 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20850 for (var e = 0; e < c.events.length; e++){
20851 var ev = c.events[e];
20852 var rows = ev.rows;
20854 for(var i = 0; i < rows.length; i++) {
20856 // how many rows should it span..
20859 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20860 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20862 unselectable : "on",
20865 cls: 'fc-event-inner',
20869 // cls: 'fc-event-time',
20870 // html : cells.length > 1 ? '' : ev.time
20874 cls: 'fc-event-title',
20875 html : String.format('{0}', ev.title)
20882 cls: 'ui-resizable-handle ui-resizable-e',
20883 html : '  '
20890 cfg.cls += ' fc-event-start';
20892 if ((i+1) == rows.length) {
20893 cfg.cls += ' fc-event-end';
20896 var ctr = _this.el.select('.fc-event-container',true).first();
20897 var cg = ctr.createChild(cfg);
20899 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20900 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20902 var r = (c.more.length) ? 1 : 0;
20903 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20904 cg.setWidth(ebox.right - sbox.x -2);
20906 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20907 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20908 cg.on('click', _this.onEventClick, _this, ev);
20919 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20920 style : 'position: absolute',
20921 unselectable : "on",
20924 cls: 'fc-event-inner',
20928 cls: 'fc-event-title',
20936 cls: 'ui-resizable-handle ui-resizable-e',
20937 html : '  '
20943 var ctr = _this.el.select('.fc-event-container',true).first();
20944 var cg = ctr.createChild(cfg);
20946 var sbox = c.select('.fc-day-content',true).first().getBox();
20947 var ebox = c.select('.fc-day-content',true).first().getBox();
20949 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20950 cg.setWidth(ebox.right - sbox.x -2);
20952 cg.on('click', _this.onMoreEventClick, _this, c.more);
20962 onEventEnter: function (e, el,event,d) {
20963 this.fireEvent('evententer', this, el, event);
20966 onEventLeave: function (e, el,event,d) {
20967 this.fireEvent('eventleave', this, el, event);
20970 onEventClick: function (e, el,event,d) {
20971 this.fireEvent('eventclick', this, el, event);
20974 onMonthChange: function () {
20978 onMoreEventClick: function(e, el, more)
20982 this.calpopover.placement = 'right';
20983 this.calpopover.setTitle('More');
20985 this.calpopover.setContent('');
20987 var ctr = this.calpopover.el.select('.popover-content', true).first();
20989 Roo.each(more, function(m){
20991 cls : 'fc-event-hori fc-event-draggable',
20994 var cg = ctr.createChild(cfg);
20996 cg.on('click', _this.onEventClick, _this, m);
20999 this.calpopover.show(el);
21004 onLoad: function ()
21006 this.calevents = [];
21009 if(this.store.getCount() > 0){
21010 this.store.data.each(function(d){
21013 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21014 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21015 time : d.data.start_time,
21016 title : d.data.title,
21017 description : d.data.description,
21018 venue : d.data.venue
21023 this.renderEvents();
21025 if(this.calevents.length && this.loadMask){
21026 this.maskEl.hide();
21030 onBeforeLoad: function()
21032 this.clearEvents();
21034 this.maskEl.show();
21048 * @class Roo.bootstrap.Popover
21049 * @extends Roo.bootstrap.Component
21050 * Bootstrap Popover class
21051 * @cfg {String} html contents of the popover (or false to use children..)
21052 * @cfg {String} title of popover (or false to hide)
21053 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21054 * @cfg {String} trigger click || hover (or false to trigger manually)
21055 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21056 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21057 * - if false and it has a 'parent' then it will be automatically added to that element
21058 * - if string - Roo.get will be called
21059 * @cfg {Number} delay - delay before showing
21062 * Create a new Popover
21063 * @param {Object} config The config object
21066 Roo.bootstrap.Popover = function(config){
21067 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21073 * After the popover show
21075 * @param {Roo.bootstrap.Popover} this
21080 * After the popover hide
21082 * @param {Roo.bootstrap.Popover} this
21088 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21093 placement : 'right',
21094 trigger : 'hover', // hover
21100 can_build_overlaid : false,
21102 maskEl : false, // the mask element
21105 alignEl : false, // when show is called with an element - this get's stored.
21107 getChildContainer : function()
21109 return this.contentEl;
21112 getPopoverHeader : function()
21114 this.title = true; // flag not to hide it..
21115 this.headerEl.addClass('p-0');
21116 return this.headerEl
21120 getAutoCreate : function(){
21123 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21124 style: 'display:block',
21130 cls : 'popover-inner ',
21134 cls: 'popover-title popover-header',
21135 html : this.title === false ? '' : this.title
21138 cls : 'popover-content popover-body ' + (this.cls || ''),
21139 html : this.html || ''
21150 * @param {string} the title
21152 setTitle: function(str)
21156 this.headerEl.dom.innerHTML = str;
21161 * @param {string} the body content
21163 setContent: function(str)
21166 if (this.contentEl) {
21167 this.contentEl.dom.innerHTML = str;
21171 // as it get's added to the bottom of the page.
21172 onRender : function(ct, position)
21174 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21179 var cfg = Roo.apply({}, this.getAutoCreate());
21183 cfg.cls += ' ' + this.cls;
21186 cfg.style = this.style;
21188 //Roo.log("adding to ");
21189 this.el = Roo.get(document.body).createChild(cfg, position);
21190 // Roo.log(this.el);
21193 this.contentEl = this.el.select('.popover-content',true).first();
21194 this.headerEl = this.el.select('.popover-title',true).first();
21197 if(typeof(this.items) != 'undefined'){
21198 var items = this.items;
21201 for(var i =0;i < items.length;i++) {
21202 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21206 this.items = nitems;
21208 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21209 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21216 resizeMask : function()
21218 this.maskEl.setSize(
21219 Roo.lib.Dom.getViewWidth(true),
21220 Roo.lib.Dom.getViewHeight(true)
21224 initEvents : function()
21228 Roo.bootstrap.Popover.register(this);
21231 this.arrowEl = this.el.select('.arrow',true).first();
21232 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21233 this.el.enableDisplayMode('block');
21237 if (this.over === false && !this.parent()) {
21240 if (this.triggers === false) {
21245 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21246 var triggers = this.trigger ? this.trigger.split(' ') : [];
21247 Roo.each(triggers, function(trigger) {
21249 if (trigger == 'click') {
21250 on_el.on('click', this.toggle, this);
21251 } else if (trigger != 'manual') {
21252 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21253 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21255 on_el.on(eventIn ,this.enter, this);
21256 on_el.on(eventOut, this.leave, this);
21266 toggle : function () {
21267 this.hoverState == 'in' ? this.leave() : this.enter();
21270 enter : function () {
21272 clearTimeout(this.timeout);
21274 this.hoverState = 'in';
21276 if (!this.delay || !this.delay.show) {
21281 this.timeout = setTimeout(function () {
21282 if (_t.hoverState == 'in') {
21285 }, this.delay.show)
21288 leave : function() {
21289 clearTimeout(this.timeout);
21291 this.hoverState = 'out';
21293 if (!this.delay || !this.delay.hide) {
21298 this.timeout = setTimeout(function () {
21299 if (_t.hoverState == 'out') {
21302 }, this.delay.hide)
21306 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21307 * @param {string} (left|right|top|bottom) position
21309 show : function (on_el, placement)
21311 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21312 on_el = on_el || false; // default to false
21315 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21316 on_el = this.parent().el;
21317 } else if (this.over) {
21318 on_el = Roo.get(this.over);
21323 this.alignEl = Roo.get( on_el );
21326 this.render(document.body);
21332 if (this.title === false) {
21333 this.headerEl.hide();
21338 this.el.dom.style.display = 'block';
21341 if (this.alignEl) {
21342 this.updatePosition(this.placement, true);
21345 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21346 var es = this.el.getSize();
21347 var x = Roo.lib.Dom.getViewWidth()/2;
21348 var y = Roo.lib.Dom.getViewHeight()/2;
21349 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21354 //var arrow = this.el.select('.arrow',true).first();
21355 //arrow.set(align[2],
21357 this.el.addClass('in');
21361 this.hoverState = 'in';
21364 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21365 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21366 this.maskEl.dom.style.display = 'block';
21367 this.maskEl.addClass('show');
21369 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21371 this.fireEvent('show', this);
21375 * fire this manually after loading a grid in the table for example
21376 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21377 * @param {Boolean} try and move it if we cant get right position.
21379 updatePosition : function(placement, try_move)
21381 // allow for calling with no parameters
21382 placement = placement ? placement : this.placement;
21383 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21385 this.el.removeClass([
21386 'fade','top','bottom', 'left', 'right','in',
21387 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21389 this.el.addClass(placement + ' bs-popover-' + placement);
21391 if (!this.alignEl ) {
21395 switch (placement) {
21397 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21398 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21399 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21400 //normal display... or moved up/down.
21401 this.el.setXY(offset);
21402 var xy = this.alignEl.getAnchorXY('tr', false);
21404 this.arrowEl.setXY(xy);
21407 // continue through...
21408 return this.updatePosition('left', false);
21412 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21413 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21414 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21415 //normal display... or moved up/down.
21416 this.el.setXY(offset);
21417 var xy = this.alignEl.getAnchorXY('tl', false);
21418 xy[0]-=10;xy[1]+=5; // << fix me
21419 this.arrowEl.setXY(xy);
21423 return this.updatePosition('right', false);
21426 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21427 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21428 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21429 //normal display... or moved up/down.
21430 this.el.setXY(offset);
21431 var xy = this.alignEl.getAnchorXY('t', false);
21432 xy[1]-=10; // << fix me
21433 this.arrowEl.setXY(xy);
21437 return this.updatePosition('bottom', false);
21440 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21441 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21442 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21443 //normal display... or moved up/down.
21444 this.el.setXY(offset);
21445 var xy = this.alignEl.getAnchorXY('b', false);
21446 xy[1]+=2; // << fix me
21447 this.arrowEl.setXY(xy);
21451 return this.updatePosition('top', false);
21462 this.el.setXY([0,0]);
21463 this.el.removeClass('in');
21465 this.hoverState = null;
21466 this.maskEl.hide(); // always..
21467 this.fireEvent('hide', this);
21473 Roo.apply(Roo.bootstrap.Popover, {
21476 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21477 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21478 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21479 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21484 clickHander : false,
21488 onMouseDown : function(e)
21490 if (this.popups.length && !e.getTarget(".roo-popover")) {
21491 /// what is nothing is showing..
21500 register : function(popup)
21502 if (!Roo.bootstrap.Popover.clickHandler) {
21503 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21505 // hide other popups.
21506 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21507 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21508 this.hideAll(); //<< why?
21509 //this.popups.push(popup);
21511 hideAll : function()
21513 this.popups.forEach(function(p) {
21517 onShow : function() {
21518 Roo.bootstrap.Popover.popups.push(this);
21520 onHide : function() {
21521 Roo.bootstrap.Popover.popups.remove(this);
21527 * Card header - holder for the card header elements.
21532 * @class Roo.bootstrap.PopoverNav
21533 * @extends Roo.bootstrap.NavGroup
21534 * Bootstrap Popover header navigation class
21536 * Create a new Popover Header Navigation
21537 * @param {Object} config The config object
21540 Roo.bootstrap.PopoverNav = function(config){
21541 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21544 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21547 container_method : 'getPopoverHeader'
21565 * @class Roo.bootstrap.Progress
21566 * @extends Roo.bootstrap.Component
21567 * Bootstrap Progress class
21568 * @cfg {Boolean} striped striped of the progress bar
21569 * @cfg {Boolean} active animated of the progress bar
21573 * Create a new Progress
21574 * @param {Object} config The config object
21577 Roo.bootstrap.Progress = function(config){
21578 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21581 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21586 getAutoCreate : function(){
21594 cfg.cls += ' progress-striped';
21598 cfg.cls += ' active';
21617 * @class Roo.bootstrap.ProgressBar
21618 * @extends Roo.bootstrap.Component
21619 * Bootstrap ProgressBar class
21620 * @cfg {Number} aria_valuenow aria-value now
21621 * @cfg {Number} aria_valuemin aria-value min
21622 * @cfg {Number} aria_valuemax aria-value max
21623 * @cfg {String} label label for the progress bar
21624 * @cfg {String} panel (success | info | warning | danger )
21625 * @cfg {String} role role of the progress bar
21626 * @cfg {String} sr_only text
21630 * Create a new ProgressBar
21631 * @param {Object} config The config object
21634 Roo.bootstrap.ProgressBar = function(config){
21635 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21638 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21642 aria_valuemax : 100,
21648 getAutoCreate : function()
21653 cls: 'progress-bar',
21654 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21666 cfg.role = this.role;
21669 if(this.aria_valuenow){
21670 cfg['aria-valuenow'] = this.aria_valuenow;
21673 if(this.aria_valuemin){
21674 cfg['aria-valuemin'] = this.aria_valuemin;
21677 if(this.aria_valuemax){
21678 cfg['aria-valuemax'] = this.aria_valuemax;
21681 if(this.label && !this.sr_only){
21682 cfg.html = this.label;
21686 cfg.cls += ' progress-bar-' + this.panel;
21692 update : function(aria_valuenow)
21694 this.aria_valuenow = aria_valuenow;
21696 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21711 * @class Roo.bootstrap.TabGroup
21712 * @extends Roo.bootstrap.Column
21713 * Bootstrap Column class
21714 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21715 * @cfg {Boolean} carousel true to make the group behave like a carousel
21716 * @cfg {Boolean} bullets show bullets for the panels
21717 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21718 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21719 * @cfg {Boolean} showarrow (true|false) show arrow default true
21722 * Create a new TabGroup
21723 * @param {Object} config The config object
21726 Roo.bootstrap.TabGroup = function(config){
21727 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21729 this.navId = Roo.id();
21732 Roo.bootstrap.TabGroup.register(this);
21736 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21739 transition : false,
21744 slideOnTouch : false,
21747 getAutoCreate : function()
21749 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21751 cfg.cls += ' tab-content';
21753 if (this.carousel) {
21754 cfg.cls += ' carousel slide';
21757 cls : 'carousel-inner',
21761 if(this.bullets && !Roo.isTouch){
21764 cls : 'carousel-bullets',
21768 if(this.bullets_cls){
21769 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21776 cfg.cn[0].cn.push(bullets);
21779 if(this.showarrow){
21780 cfg.cn[0].cn.push({
21782 class : 'carousel-arrow',
21786 class : 'carousel-prev',
21790 class : 'fa fa-chevron-left'
21796 class : 'carousel-next',
21800 class : 'fa fa-chevron-right'
21813 initEvents: function()
21815 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21816 // this.el.on("touchstart", this.onTouchStart, this);
21819 if(this.autoslide){
21822 this.slideFn = window.setInterval(function() {
21823 _this.showPanelNext();
21827 if(this.showarrow){
21828 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21829 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21835 // onTouchStart : function(e, el, o)
21837 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21841 // this.showPanelNext();
21845 getChildContainer : function()
21847 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21851 * register a Navigation item
21852 * @param {Roo.bootstrap.NavItem} the navitem to add
21854 register : function(item)
21856 this.tabs.push( item);
21857 item.navId = this.navId; // not really needed..
21862 getActivePanel : function()
21865 Roo.each(this.tabs, function(t) {
21875 getPanelByName : function(n)
21878 Roo.each(this.tabs, function(t) {
21879 if (t.tabId == n) {
21887 indexOfPanel : function(p)
21890 Roo.each(this.tabs, function(t,i) {
21891 if (t.tabId == p.tabId) {
21900 * show a specific panel
21901 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21902 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21904 showPanel : function (pan)
21906 if(this.transition || typeof(pan) == 'undefined'){
21907 Roo.log("waiting for the transitionend");
21911 if (typeof(pan) == 'number') {
21912 pan = this.tabs[pan];
21915 if (typeof(pan) == 'string') {
21916 pan = this.getPanelByName(pan);
21919 var cur = this.getActivePanel();
21922 Roo.log('pan or acitve pan is undefined');
21926 if (pan.tabId == this.getActivePanel().tabId) {
21930 if (false === cur.fireEvent('beforedeactivate')) {
21934 if(this.bullets > 0 && !Roo.isTouch){
21935 this.setActiveBullet(this.indexOfPanel(pan));
21938 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21940 //class="carousel-item carousel-item-next carousel-item-left"
21942 this.transition = true;
21943 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21944 var lr = dir == 'next' ? 'left' : 'right';
21945 pan.el.addClass(dir); // or prev
21946 pan.el.addClass('carousel-item-' + dir); // or prev
21947 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21948 cur.el.addClass(lr); // or right
21949 pan.el.addClass(lr);
21950 cur.el.addClass('carousel-item-' +lr); // or right
21951 pan.el.addClass('carousel-item-' +lr);
21955 cur.el.on('transitionend', function() {
21956 Roo.log("trans end?");
21958 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21959 pan.setActive(true);
21961 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21962 cur.setActive(false);
21964 _this.transition = false;
21966 }, this, { single: true } );
21971 cur.setActive(false);
21972 pan.setActive(true);
21977 showPanelNext : function()
21979 var i = this.indexOfPanel(this.getActivePanel());
21981 if (i >= this.tabs.length - 1 && !this.autoslide) {
21985 if (i >= this.tabs.length - 1 && this.autoslide) {
21989 this.showPanel(this.tabs[i+1]);
21992 showPanelPrev : function()
21994 var i = this.indexOfPanel(this.getActivePanel());
21996 if (i < 1 && !this.autoslide) {
22000 if (i < 1 && this.autoslide) {
22001 i = this.tabs.length;
22004 this.showPanel(this.tabs[i-1]);
22008 addBullet: function()
22010 if(!this.bullets || Roo.isTouch){
22013 var ctr = this.el.select('.carousel-bullets',true).first();
22014 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22015 var bullet = ctr.createChild({
22016 cls : 'bullet bullet-' + i
22017 },ctr.dom.lastChild);
22022 bullet.on('click', (function(e, el, o, ii, t){
22024 e.preventDefault();
22026 this.showPanel(ii);
22028 if(this.autoslide && this.slideFn){
22029 clearInterval(this.slideFn);
22030 this.slideFn = window.setInterval(function() {
22031 _this.showPanelNext();
22035 }).createDelegate(this, [i, bullet], true));
22040 setActiveBullet : function(i)
22046 Roo.each(this.el.select('.bullet', true).elements, function(el){
22047 el.removeClass('selected');
22050 var bullet = this.el.select('.bullet-' + i, true).first();
22056 bullet.addClass('selected');
22067 Roo.apply(Roo.bootstrap.TabGroup, {
22071 * register a Navigation Group
22072 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22074 register : function(navgrp)
22076 this.groups[navgrp.navId] = navgrp;
22080 * fetch a Navigation Group based on the navigation ID
22081 * if one does not exist , it will get created.
22082 * @param {string} the navgroup to add
22083 * @returns {Roo.bootstrap.NavGroup} the navgroup
22085 get: function(navId) {
22086 if (typeof(this.groups[navId]) == 'undefined') {
22087 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22089 return this.groups[navId] ;
22104 * @class Roo.bootstrap.TabPanel
22105 * @extends Roo.bootstrap.Component
22106 * Bootstrap TabPanel class
22107 * @cfg {Boolean} active panel active
22108 * @cfg {String} html panel content
22109 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22110 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22111 * @cfg {String} href click to link..
22112 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22116 * Create a new TabPanel
22117 * @param {Object} config The config object
22120 Roo.bootstrap.TabPanel = function(config){
22121 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22125 * Fires when the active status changes
22126 * @param {Roo.bootstrap.TabPanel} this
22127 * @param {Boolean} state the new state
22132 * @event beforedeactivate
22133 * Fires before a tab is de-activated - can be used to do validation on a form.
22134 * @param {Roo.bootstrap.TabPanel} this
22135 * @return {Boolean} false if there is an error
22138 'beforedeactivate': true
22141 this.tabId = this.tabId || Roo.id();
22145 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22152 touchSlide : false,
22153 getAutoCreate : function(){
22158 // item is needed for carousel - not sure if it has any effect otherwise
22159 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22160 html: this.html || ''
22164 cfg.cls += ' active';
22168 cfg.tabId = this.tabId;
22176 initEvents: function()
22178 var p = this.parent();
22180 this.navId = this.navId || p.navId;
22182 if (typeof(this.navId) != 'undefined') {
22183 // not really needed.. but just in case.. parent should be a NavGroup.
22184 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22188 var i = tg.tabs.length - 1;
22190 if(this.active && tg.bullets > 0 && i < tg.bullets){
22191 tg.setActiveBullet(i);
22195 this.el.on('click', this.onClick, this);
22197 if(Roo.isTouch && this.touchSlide){
22198 this.el.on("touchstart", this.onTouchStart, this);
22199 this.el.on("touchmove", this.onTouchMove, this);
22200 this.el.on("touchend", this.onTouchEnd, this);
22205 onRender : function(ct, position)
22207 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22210 setActive : function(state)
22212 Roo.log("panel - set active " + this.tabId + "=" + state);
22214 this.active = state;
22216 this.el.removeClass('active');
22218 } else if (!this.el.hasClass('active')) {
22219 this.el.addClass('active');
22222 this.fireEvent('changed', this, state);
22225 onClick : function(e)
22227 e.preventDefault();
22229 if(!this.href.length){
22233 window.location.href = this.href;
22242 onTouchStart : function(e)
22244 this.swiping = false;
22246 this.startX = e.browserEvent.touches[0].clientX;
22247 this.startY = e.browserEvent.touches[0].clientY;
22250 onTouchMove : function(e)
22252 this.swiping = true;
22254 this.endX = e.browserEvent.touches[0].clientX;
22255 this.endY = e.browserEvent.touches[0].clientY;
22258 onTouchEnd : function(e)
22265 var tabGroup = this.parent();
22267 if(this.endX > this.startX){ // swiping right
22268 tabGroup.showPanelPrev();
22272 if(this.startX > this.endX){ // swiping left
22273 tabGroup.showPanelNext();
22292 * @class Roo.bootstrap.DateField
22293 * @extends Roo.bootstrap.Input
22294 * Bootstrap DateField class
22295 * @cfg {Number} weekStart default 0
22296 * @cfg {String} viewMode default empty, (months|years)
22297 * @cfg {String} minViewMode default empty, (months|years)
22298 * @cfg {Number} startDate default -Infinity
22299 * @cfg {Number} endDate default Infinity
22300 * @cfg {Boolean} todayHighlight default false
22301 * @cfg {Boolean} todayBtn default false
22302 * @cfg {Boolean} calendarWeeks default false
22303 * @cfg {Object} daysOfWeekDisabled default empty
22304 * @cfg {Boolean} singleMode default false (true | false)
22306 * @cfg {Boolean} keyboardNavigation default true
22307 * @cfg {String} language default en
22310 * Create a new DateField
22311 * @param {Object} config The config object
22314 Roo.bootstrap.DateField = function(config){
22315 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22319 * Fires when this field show.
22320 * @param {Roo.bootstrap.DateField} this
22321 * @param {Mixed} date The date value
22326 * Fires when this field hide.
22327 * @param {Roo.bootstrap.DateField} this
22328 * @param {Mixed} date The date value
22333 * Fires when select a date.
22334 * @param {Roo.bootstrap.DateField} this
22335 * @param {Mixed} date The date value
22339 * @event beforeselect
22340 * Fires when before select a date.
22341 * @param {Roo.bootstrap.DateField} this
22342 * @param {Mixed} date The date value
22344 beforeselect : true
22348 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22351 * @cfg {String} format
22352 * The default date format string which can be overriden for localization support. The format must be
22353 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22357 * @cfg {String} altFormats
22358 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22359 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22361 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22369 todayHighlight : false,
22375 keyboardNavigation: true,
22377 calendarWeeks: false,
22379 startDate: -Infinity,
22383 daysOfWeekDisabled: [],
22387 singleMode : false,
22389 UTCDate: function()
22391 return new Date(Date.UTC.apply(Date, arguments));
22394 UTCToday: function()
22396 var today = new Date();
22397 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22400 getDate: function() {
22401 var d = this.getUTCDate();
22402 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22405 getUTCDate: function() {
22409 setDate: function(d) {
22410 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22413 setUTCDate: function(d) {
22415 this.setValue(this.formatDate(this.date));
22418 onRender: function(ct, position)
22421 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22423 this.language = this.language || 'en';
22424 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22425 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22427 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22428 this.format = this.format || 'm/d/y';
22429 this.isInline = false;
22430 this.isInput = true;
22431 this.component = this.el.select('.add-on', true).first() || false;
22432 this.component = (this.component && this.component.length === 0) ? false : this.component;
22433 this.hasInput = this.component && this.inputEl().length;
22435 if (typeof(this.minViewMode === 'string')) {
22436 switch (this.minViewMode) {
22438 this.minViewMode = 1;
22441 this.minViewMode = 2;
22444 this.minViewMode = 0;
22449 if (typeof(this.viewMode === 'string')) {
22450 switch (this.viewMode) {
22463 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22465 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22467 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22469 this.picker().on('mousedown', this.onMousedown, this);
22470 this.picker().on('click', this.onClick, this);
22472 this.picker().addClass('datepicker-dropdown');
22474 this.startViewMode = this.viewMode;
22476 if(this.singleMode){
22477 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22478 v.setVisibilityMode(Roo.Element.DISPLAY);
22482 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22483 v.setStyle('width', '189px');
22487 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22488 if(!this.calendarWeeks){
22493 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22494 v.attr('colspan', function(i, val){
22495 return parseInt(val) + 1;
22500 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22502 this.setStartDate(this.startDate);
22503 this.setEndDate(this.endDate);
22505 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22512 if(this.isInline) {
22517 picker : function()
22519 return this.pickerEl;
22520 // return this.el.select('.datepicker', true).first();
22523 fillDow: function()
22525 var dowCnt = this.weekStart;
22534 if(this.calendarWeeks){
22542 while (dowCnt < this.weekStart + 7) {
22546 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22550 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22553 fillMonths: function()
22556 var months = this.picker().select('>.datepicker-months td', true).first();
22558 months.dom.innerHTML = '';
22564 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22567 months.createChild(month);
22574 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;
22576 if (this.date < this.startDate) {
22577 this.viewDate = new Date(this.startDate);
22578 } else if (this.date > this.endDate) {
22579 this.viewDate = new Date(this.endDate);
22581 this.viewDate = new Date(this.date);
22589 var d = new Date(this.viewDate),
22590 year = d.getUTCFullYear(),
22591 month = d.getUTCMonth(),
22592 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22593 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22594 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22595 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22596 currentDate = this.date && this.date.valueOf(),
22597 today = this.UTCToday();
22599 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22601 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22603 // this.picker.select('>tfoot th.today').
22604 // .text(dates[this.language].today)
22605 // .toggle(this.todayBtn !== false);
22607 this.updateNavArrows();
22610 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22612 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22614 prevMonth.setUTCDate(day);
22616 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22618 var nextMonth = new Date(prevMonth);
22620 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22622 nextMonth = nextMonth.valueOf();
22624 var fillMonths = false;
22626 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22628 while(prevMonth.valueOf() <= nextMonth) {
22631 if (prevMonth.getUTCDay() === this.weekStart) {
22633 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22641 if(this.calendarWeeks){
22642 // ISO 8601: First week contains first thursday.
22643 // ISO also states week starts on Monday, but we can be more abstract here.
22645 // Start of current week: based on weekstart/current date
22646 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22647 // Thursday of this week
22648 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22649 // First Thursday of year, year from thursday
22650 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22651 // Calendar week: ms between thursdays, div ms per day, div 7 days
22652 calWeek = (th - yth) / 864e5 / 7 + 1;
22654 fillMonths.cn.push({
22662 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22664 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22667 if (this.todayHighlight &&
22668 prevMonth.getUTCFullYear() == today.getFullYear() &&
22669 prevMonth.getUTCMonth() == today.getMonth() &&
22670 prevMonth.getUTCDate() == today.getDate()) {
22671 clsName += ' today';
22674 if (currentDate && prevMonth.valueOf() === currentDate) {
22675 clsName += ' active';
22678 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22679 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22680 clsName += ' disabled';
22683 fillMonths.cn.push({
22685 cls: 'day ' + clsName,
22686 html: prevMonth.getDate()
22689 prevMonth.setDate(prevMonth.getDate()+1);
22692 var currentYear = this.date && this.date.getUTCFullYear();
22693 var currentMonth = this.date && this.date.getUTCMonth();
22695 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22697 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22698 v.removeClass('active');
22700 if(currentYear === year && k === currentMonth){
22701 v.addClass('active');
22704 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22705 v.addClass('disabled');
22711 year = parseInt(year/10, 10) * 10;
22713 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22715 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22718 for (var i = -1; i < 11; i++) {
22719 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22721 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22729 showMode: function(dir)
22732 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22735 Roo.each(this.picker().select('>div',true).elements, function(v){
22736 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22739 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22744 if(this.isInline) {
22748 this.picker().removeClass(['bottom', 'top']);
22750 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22752 * place to the top of element!
22756 this.picker().addClass('top');
22757 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22762 this.picker().addClass('bottom');
22764 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22767 parseDate : function(value)
22769 if(!value || value instanceof Date){
22772 var v = Date.parseDate(value, this.format);
22773 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22774 v = Date.parseDate(value, 'Y-m-d');
22776 if(!v && this.altFormats){
22777 if(!this.altFormatsArray){
22778 this.altFormatsArray = this.altFormats.split("|");
22780 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22781 v = Date.parseDate(value, this.altFormatsArray[i]);
22787 formatDate : function(date, fmt)
22789 return (!date || !(date instanceof Date)) ?
22790 date : date.dateFormat(fmt || this.format);
22793 onFocus : function()
22795 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22799 onBlur : function()
22801 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22803 var d = this.inputEl().getValue();
22810 showPopup : function()
22812 this.picker().show();
22816 this.fireEvent('showpopup', this, this.date);
22819 hidePopup : function()
22821 if(this.isInline) {
22824 this.picker().hide();
22825 this.viewMode = this.startViewMode;
22828 this.fireEvent('hidepopup', this, this.date);
22832 onMousedown: function(e)
22834 e.stopPropagation();
22835 e.preventDefault();
22840 Roo.bootstrap.DateField.superclass.keyup.call(this);
22844 setValue: function(v)
22846 if(this.fireEvent('beforeselect', this, v) !== false){
22847 var d = new Date(this.parseDate(v) ).clearTime();
22849 if(isNaN(d.getTime())){
22850 this.date = this.viewDate = '';
22851 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22855 v = this.formatDate(d);
22857 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22859 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22863 this.fireEvent('select', this, this.date);
22867 getValue: function()
22869 return this.formatDate(this.date);
22872 fireKey: function(e)
22874 if (!this.picker().isVisible()){
22875 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22881 var dateChanged = false,
22883 newDate, newViewDate;
22888 e.preventDefault();
22892 if (!this.keyboardNavigation) {
22895 dir = e.keyCode == 37 ? -1 : 1;
22898 newDate = this.moveYear(this.date, dir);
22899 newViewDate = this.moveYear(this.viewDate, dir);
22900 } else if (e.shiftKey){
22901 newDate = this.moveMonth(this.date, dir);
22902 newViewDate = this.moveMonth(this.viewDate, dir);
22904 newDate = new Date(this.date);
22905 newDate.setUTCDate(this.date.getUTCDate() + dir);
22906 newViewDate = new Date(this.viewDate);
22907 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22909 if (this.dateWithinRange(newDate)){
22910 this.date = newDate;
22911 this.viewDate = newViewDate;
22912 this.setValue(this.formatDate(this.date));
22914 e.preventDefault();
22915 dateChanged = true;
22920 if (!this.keyboardNavigation) {
22923 dir = e.keyCode == 38 ? -1 : 1;
22925 newDate = this.moveYear(this.date, dir);
22926 newViewDate = this.moveYear(this.viewDate, dir);
22927 } else if (e.shiftKey){
22928 newDate = this.moveMonth(this.date, dir);
22929 newViewDate = this.moveMonth(this.viewDate, dir);
22931 newDate = new Date(this.date);
22932 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22933 newViewDate = new Date(this.viewDate);
22934 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22936 if (this.dateWithinRange(newDate)){
22937 this.date = newDate;
22938 this.viewDate = newViewDate;
22939 this.setValue(this.formatDate(this.date));
22941 e.preventDefault();
22942 dateChanged = true;
22946 this.setValue(this.formatDate(this.date));
22948 e.preventDefault();
22951 this.setValue(this.formatDate(this.date));
22965 onClick: function(e)
22967 e.stopPropagation();
22968 e.preventDefault();
22970 var target = e.getTarget();
22972 if(target.nodeName.toLowerCase() === 'i'){
22973 target = Roo.get(target).dom.parentNode;
22976 var nodeName = target.nodeName;
22977 var className = target.className;
22978 var html = target.innerHTML;
22979 //Roo.log(nodeName);
22981 switch(nodeName.toLowerCase()) {
22983 switch(className) {
22989 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22990 switch(this.viewMode){
22992 this.viewDate = this.moveMonth(this.viewDate, dir);
22996 this.viewDate = this.moveYear(this.viewDate, dir);
23002 var date = new Date();
23003 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23005 this.setValue(this.formatDate(this.date));
23012 if (className.indexOf('disabled') < 0) {
23013 if (!this.viewDate) {
23014 this.viewDate = new Date();
23016 this.viewDate.setUTCDate(1);
23017 if (className.indexOf('month') > -1) {
23018 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23020 var year = parseInt(html, 10) || 0;
23021 this.viewDate.setUTCFullYear(year);
23025 if(this.singleMode){
23026 this.setValue(this.formatDate(this.viewDate));
23037 //Roo.log(className);
23038 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23039 var day = parseInt(html, 10) || 1;
23040 var year = (this.viewDate || new Date()).getUTCFullYear(),
23041 month = (this.viewDate || new Date()).getUTCMonth();
23043 if (className.indexOf('old') > -1) {
23050 } else if (className.indexOf('new') > -1) {
23058 //Roo.log([year,month,day]);
23059 this.date = this.UTCDate(year, month, day,0,0,0,0);
23060 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23062 //Roo.log(this.formatDate(this.date));
23063 this.setValue(this.formatDate(this.date));
23070 setStartDate: function(startDate)
23072 this.startDate = startDate || -Infinity;
23073 if (this.startDate !== -Infinity) {
23074 this.startDate = this.parseDate(this.startDate);
23077 this.updateNavArrows();
23080 setEndDate: function(endDate)
23082 this.endDate = endDate || Infinity;
23083 if (this.endDate !== Infinity) {
23084 this.endDate = this.parseDate(this.endDate);
23087 this.updateNavArrows();
23090 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23092 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23093 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23094 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23096 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23097 return parseInt(d, 10);
23100 this.updateNavArrows();
23103 updateNavArrows: function()
23105 if(this.singleMode){
23109 var d = new Date(this.viewDate),
23110 year = d.getUTCFullYear(),
23111 month = d.getUTCMonth();
23113 Roo.each(this.picker().select('.prev', true).elements, function(v){
23115 switch (this.viewMode) {
23118 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23124 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23131 Roo.each(this.picker().select('.next', true).elements, function(v){
23133 switch (this.viewMode) {
23136 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23142 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23150 moveMonth: function(date, dir)
23155 var new_date = new Date(date.valueOf()),
23156 day = new_date.getUTCDate(),
23157 month = new_date.getUTCMonth(),
23158 mag = Math.abs(dir),
23160 dir = dir > 0 ? 1 : -1;
23163 // If going back one month, make sure month is not current month
23164 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23166 return new_date.getUTCMonth() == month;
23168 // If going forward one month, make sure month is as expected
23169 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23171 return new_date.getUTCMonth() != new_month;
23173 new_month = month + dir;
23174 new_date.setUTCMonth(new_month);
23175 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23176 if (new_month < 0 || new_month > 11) {
23177 new_month = (new_month + 12) % 12;
23180 // For magnitudes >1, move one month at a time...
23181 for (var i=0; i<mag; i++) {
23182 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23183 new_date = this.moveMonth(new_date, dir);
23185 // ...then reset the day, keeping it in the new month
23186 new_month = new_date.getUTCMonth();
23187 new_date.setUTCDate(day);
23189 return new_month != new_date.getUTCMonth();
23192 // Common date-resetting loop -- if date is beyond end of month, make it
23195 new_date.setUTCDate(--day);
23196 new_date.setUTCMonth(new_month);
23201 moveYear: function(date, dir)
23203 return this.moveMonth(date, dir*12);
23206 dateWithinRange: function(date)
23208 return date >= this.startDate && date <= this.endDate;
23214 this.picker().remove();
23217 validateValue : function(value)
23219 if(this.getVisibilityEl().hasClass('hidden')){
23223 if(value.length < 1) {
23224 if(this.allowBlank){
23230 if(value.length < this.minLength){
23233 if(value.length > this.maxLength){
23237 var vt = Roo.form.VTypes;
23238 if(!vt[this.vtype](value, this)){
23242 if(typeof this.validator == "function"){
23243 var msg = this.validator(value);
23249 if(this.regex && !this.regex.test(value)){
23253 if(typeof(this.parseDate(value)) == 'undefined'){
23257 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23261 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23271 this.date = this.viewDate = '';
23273 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23278 Roo.apply(Roo.bootstrap.DateField, {
23289 html: '<i class="fa fa-arrow-left"/>'
23299 html: '<i class="fa fa-arrow-right"/>'
23341 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23342 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23343 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23344 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23345 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23358 navFnc: 'FullYear',
23363 navFnc: 'FullYear',
23368 Roo.apply(Roo.bootstrap.DateField, {
23372 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23376 cls: 'datepicker-days',
23380 cls: 'table-condensed',
23382 Roo.bootstrap.DateField.head,
23386 Roo.bootstrap.DateField.footer
23393 cls: 'datepicker-months',
23397 cls: 'table-condensed',
23399 Roo.bootstrap.DateField.head,
23400 Roo.bootstrap.DateField.content,
23401 Roo.bootstrap.DateField.footer
23408 cls: 'datepicker-years',
23412 cls: 'table-condensed',
23414 Roo.bootstrap.DateField.head,
23415 Roo.bootstrap.DateField.content,
23416 Roo.bootstrap.DateField.footer
23435 * @class Roo.bootstrap.TimeField
23436 * @extends Roo.bootstrap.Input
23437 * Bootstrap DateField class
23441 * Create a new TimeField
23442 * @param {Object} config The config object
23445 Roo.bootstrap.TimeField = function(config){
23446 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23450 * Fires when this field show.
23451 * @param {Roo.bootstrap.DateField} thisthis
23452 * @param {Mixed} date The date value
23457 * Fires when this field hide.
23458 * @param {Roo.bootstrap.DateField} this
23459 * @param {Mixed} date The date value
23464 * Fires when select a date.
23465 * @param {Roo.bootstrap.DateField} this
23466 * @param {Mixed} date The date value
23472 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23475 * @cfg {String} format
23476 * The default time format string which can be overriden for localization support. The format must be
23477 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23481 getAutoCreate : function()
23483 this.after = '<i class="fa far fa-clock"></i>';
23484 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23488 onRender: function(ct, position)
23491 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23493 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23495 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23497 this.pop = this.picker().select('>.datepicker-time',true).first();
23498 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23500 this.picker().on('mousedown', this.onMousedown, this);
23501 this.picker().on('click', this.onClick, this);
23503 this.picker().addClass('datepicker-dropdown');
23508 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23509 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23510 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23511 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23512 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23513 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23517 fireKey: function(e){
23518 if (!this.picker().isVisible()){
23519 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23525 e.preventDefault();
23533 this.onTogglePeriod();
23536 this.onIncrementMinutes();
23539 this.onDecrementMinutes();
23548 onClick: function(e) {
23549 e.stopPropagation();
23550 e.preventDefault();
23553 picker : function()
23555 return this.pickerEl;
23558 fillTime: function()
23560 var time = this.pop.select('tbody', true).first();
23562 time.dom.innerHTML = '';
23577 cls: 'hours-up fa fas fa-chevron-up'
23597 cls: 'minutes-up fa fas fa-chevron-up'
23618 cls: 'timepicker-hour',
23633 cls: 'timepicker-minute',
23648 cls: 'btn btn-primary period',
23670 cls: 'hours-down fa fas fa-chevron-down'
23690 cls: 'minutes-down fa fas fa-chevron-down'
23708 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23715 var hours = this.time.getHours();
23716 var minutes = this.time.getMinutes();
23729 hours = hours - 12;
23733 hours = '0' + hours;
23737 minutes = '0' + minutes;
23740 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23741 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23742 this.pop.select('button', true).first().dom.innerHTML = period;
23748 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23750 var cls = ['bottom'];
23752 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23759 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23763 //this.picker().setXY(20000,20000);
23764 this.picker().addClass(cls.join('-'));
23768 Roo.each(cls, function(c){
23773 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23774 //_this.picker().setTop(_this.inputEl().getHeight());
23778 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23780 //_this.picker().setTop(0 - _this.picker().getHeight());
23785 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23789 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23797 onFocus : function()
23799 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23803 onBlur : function()
23805 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23811 this.picker().show();
23816 this.fireEvent('show', this, this.date);
23821 this.picker().hide();
23824 this.fireEvent('hide', this, this.date);
23827 setTime : function()
23830 this.setValue(this.time.format(this.format));
23832 this.fireEvent('select', this, this.date);
23837 onMousedown: function(e){
23838 e.stopPropagation();
23839 e.preventDefault();
23842 onIncrementHours: function()
23844 Roo.log('onIncrementHours');
23845 this.time = this.time.add(Date.HOUR, 1);
23850 onDecrementHours: function()
23852 Roo.log('onDecrementHours');
23853 this.time = this.time.add(Date.HOUR, -1);
23857 onIncrementMinutes: function()
23859 Roo.log('onIncrementMinutes');
23860 this.time = this.time.add(Date.MINUTE, 1);
23864 onDecrementMinutes: function()
23866 Roo.log('onDecrementMinutes');
23867 this.time = this.time.add(Date.MINUTE, -1);
23871 onTogglePeriod: function()
23873 Roo.log('onTogglePeriod');
23874 this.time = this.time.add(Date.HOUR, 12);
23882 Roo.apply(Roo.bootstrap.TimeField, {
23886 cls: 'datepicker dropdown-menu',
23890 cls: 'datepicker-time',
23894 cls: 'table-condensed',
23923 cls: 'btn btn-info ok',
23951 * @class Roo.bootstrap.MonthField
23952 * @extends Roo.bootstrap.Input
23953 * Bootstrap MonthField class
23955 * @cfg {String} language default en
23958 * Create a new MonthField
23959 * @param {Object} config The config object
23962 Roo.bootstrap.MonthField = function(config){
23963 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23968 * Fires when this field show.
23969 * @param {Roo.bootstrap.MonthField} this
23970 * @param {Mixed} date The date value
23975 * Fires when this field hide.
23976 * @param {Roo.bootstrap.MonthField} this
23977 * @param {Mixed} date The date value
23982 * Fires when select a date.
23983 * @param {Roo.bootstrap.MonthField} this
23984 * @param {String} oldvalue The old value
23985 * @param {String} newvalue The new value
23991 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23993 onRender: function(ct, position)
23996 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23998 this.language = this.language || 'en';
23999 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24000 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24002 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24003 this.isInline = false;
24004 this.isInput = true;
24005 this.component = this.el.select('.add-on', true).first() || false;
24006 this.component = (this.component && this.component.length === 0) ? false : this.component;
24007 this.hasInput = this.component && this.inputEL().length;
24009 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24011 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24013 this.picker().on('mousedown', this.onMousedown, this);
24014 this.picker().on('click', this.onClick, this);
24016 this.picker().addClass('datepicker-dropdown');
24018 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24019 v.setStyle('width', '189px');
24026 if(this.isInline) {
24032 setValue: function(v, suppressEvent)
24034 var o = this.getValue();
24036 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24040 if(suppressEvent !== true){
24041 this.fireEvent('select', this, o, v);
24046 getValue: function()
24051 onClick: function(e)
24053 e.stopPropagation();
24054 e.preventDefault();
24056 var target = e.getTarget();
24058 if(target.nodeName.toLowerCase() === 'i'){
24059 target = Roo.get(target).dom.parentNode;
24062 var nodeName = target.nodeName;
24063 var className = target.className;
24064 var html = target.innerHTML;
24066 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24070 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24072 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24078 picker : function()
24080 return this.pickerEl;
24083 fillMonths: function()
24086 var months = this.picker().select('>.datepicker-months td', true).first();
24088 months.dom.innerHTML = '';
24094 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24097 months.createChild(month);
24106 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24107 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24110 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24111 e.removeClass('active');
24113 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24114 e.addClass('active');
24121 if(this.isInline) {
24125 this.picker().removeClass(['bottom', 'top']);
24127 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24129 * place to the top of element!
24133 this.picker().addClass('top');
24134 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24139 this.picker().addClass('bottom');
24141 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24144 onFocus : function()
24146 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24150 onBlur : function()
24152 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24154 var d = this.inputEl().getValue();
24163 this.picker().show();
24164 this.picker().select('>.datepicker-months', true).first().show();
24168 this.fireEvent('show', this, this.date);
24173 if(this.isInline) {
24176 this.picker().hide();
24177 this.fireEvent('hide', this, this.date);
24181 onMousedown: function(e)
24183 e.stopPropagation();
24184 e.preventDefault();
24189 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24193 fireKey: function(e)
24195 if (!this.picker().isVisible()){
24196 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24207 e.preventDefault();
24211 dir = e.keyCode == 37 ? -1 : 1;
24213 this.vIndex = this.vIndex + dir;
24215 if(this.vIndex < 0){
24219 if(this.vIndex > 11){
24223 if(isNaN(this.vIndex)){
24227 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24233 dir = e.keyCode == 38 ? -1 : 1;
24235 this.vIndex = this.vIndex + dir * 4;
24237 if(this.vIndex < 0){
24241 if(this.vIndex > 11){
24245 if(isNaN(this.vIndex)){
24249 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24254 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24255 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24259 e.preventDefault();
24262 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24263 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24279 this.picker().remove();
24284 Roo.apply(Roo.bootstrap.MonthField, {
24303 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24304 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24309 Roo.apply(Roo.bootstrap.MonthField, {
24313 cls: 'datepicker dropdown-menu roo-dynamic',
24317 cls: 'datepicker-months',
24321 cls: 'table-condensed',
24323 Roo.bootstrap.DateField.content
24343 * @class Roo.bootstrap.CheckBox
24344 * @extends Roo.bootstrap.Input
24345 * Bootstrap CheckBox class
24347 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24348 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24349 * @cfg {String} boxLabel The text that appears beside the checkbox
24350 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24351 * @cfg {Boolean} checked initnal the element
24352 * @cfg {Boolean} inline inline the element (default false)
24353 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24354 * @cfg {String} tooltip label tooltip
24357 * Create a new CheckBox
24358 * @param {Object} config The config object
24361 Roo.bootstrap.CheckBox = function(config){
24362 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24367 * Fires when the element is checked or unchecked.
24368 * @param {Roo.bootstrap.CheckBox} this This input
24369 * @param {Boolean} checked The new checked value
24374 * Fires when the element is click.
24375 * @param {Roo.bootstrap.CheckBox} this This input
24382 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24384 inputType: 'checkbox',
24393 // checkbox success does not make any sense really..
24398 getAutoCreate : function()
24400 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24406 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24409 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24415 type : this.inputType,
24416 value : this.inputValue,
24417 cls : 'roo-' + this.inputType, //'form-box',
24418 placeholder : this.placeholder || ''
24422 if(this.inputType != 'radio'){
24426 cls : 'roo-hidden-value',
24427 value : this.checked ? this.inputValue : this.valueOff
24432 if (this.weight) { // Validity check?
24433 cfg.cls += " " + this.inputType + "-" + this.weight;
24436 if (this.disabled) {
24437 input.disabled=true;
24441 input.checked = this.checked;
24446 input.name = this.name;
24448 if(this.inputType != 'radio'){
24449 hidden.name = this.name;
24450 input.name = '_hidden_' + this.name;
24455 input.cls += ' input-' + this.size;
24460 ['xs','sm','md','lg'].map(function(size){
24461 if (settings[size]) {
24462 cfg.cls += ' col-' + size + '-' + settings[size];
24466 var inputblock = input;
24468 if (this.before || this.after) {
24471 cls : 'input-group',
24476 inputblock.cn.push({
24478 cls : 'input-group-addon',
24483 inputblock.cn.push(input);
24485 if(this.inputType != 'radio'){
24486 inputblock.cn.push(hidden);
24490 inputblock.cn.push({
24492 cls : 'input-group-addon',
24498 var boxLabelCfg = false;
24504 //'for': id, // box label is handled by onclick - so no for...
24506 html: this.boxLabel
24509 boxLabelCfg.tooltip = this.tooltip;
24515 if (align ==='left' && this.fieldLabel.length) {
24516 // Roo.log("left and has label");
24521 cls : 'control-label',
24522 html : this.fieldLabel
24533 cfg.cn[1].cn.push(boxLabelCfg);
24536 if(this.labelWidth > 12){
24537 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24540 if(this.labelWidth < 13 && this.labelmd == 0){
24541 this.labelmd = this.labelWidth;
24544 if(this.labellg > 0){
24545 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24546 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24549 if(this.labelmd > 0){
24550 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24551 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24554 if(this.labelsm > 0){
24555 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24556 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24559 if(this.labelxs > 0){
24560 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24561 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24564 } else if ( this.fieldLabel.length) {
24565 // Roo.log(" label");
24569 tag: this.boxLabel ? 'span' : 'label',
24571 cls: 'control-label box-input-label',
24572 //cls : 'input-group-addon',
24573 html : this.fieldLabel
24580 cfg.cn.push(boxLabelCfg);
24585 // Roo.log(" no label && no align");
24586 cfg.cn = [ inputblock ] ;
24588 cfg.cn.push(boxLabelCfg);
24596 if(this.inputType != 'radio'){
24597 cfg.cn.push(hidden);
24605 * return the real input element.
24607 inputEl: function ()
24609 return this.el.select('input.roo-' + this.inputType,true).first();
24611 hiddenEl: function ()
24613 return this.el.select('input.roo-hidden-value',true).first();
24616 labelEl: function()
24618 return this.el.select('label.control-label',true).first();
24620 /* depricated... */
24624 return this.labelEl();
24627 boxLabelEl: function()
24629 return this.el.select('label.box-label',true).first();
24632 initEvents : function()
24634 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24636 this.inputEl().on('click', this.onClick, this);
24638 if (this.boxLabel) {
24639 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24642 this.startValue = this.getValue();
24645 Roo.bootstrap.CheckBox.register(this);
24649 onClick : function(e)
24651 if(this.fireEvent('click', this, e) !== false){
24652 this.setChecked(!this.checked);
24657 setChecked : function(state,suppressEvent)
24659 this.startValue = this.getValue();
24661 if(this.inputType == 'radio'){
24663 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24664 e.dom.checked = false;
24667 this.inputEl().dom.checked = true;
24669 this.inputEl().dom.value = this.inputValue;
24671 if(suppressEvent !== true){
24672 this.fireEvent('check', this, true);
24680 this.checked = state;
24682 this.inputEl().dom.checked = state;
24685 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24687 if(suppressEvent !== true){
24688 this.fireEvent('check', this, state);
24694 getValue : function()
24696 if(this.inputType == 'radio'){
24697 return this.getGroupValue();
24700 return this.hiddenEl().dom.value;
24704 getGroupValue : function()
24706 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24710 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24713 setValue : function(v,suppressEvent)
24715 if(this.inputType == 'radio'){
24716 this.setGroupValue(v, suppressEvent);
24720 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24725 setGroupValue : function(v, suppressEvent)
24727 this.startValue = this.getValue();
24729 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24730 e.dom.checked = false;
24732 if(e.dom.value == v){
24733 e.dom.checked = true;
24737 if(suppressEvent !== true){
24738 this.fireEvent('check', this, true);
24746 validate : function()
24748 if(this.getVisibilityEl().hasClass('hidden')){
24754 (this.inputType == 'radio' && this.validateRadio()) ||
24755 (this.inputType == 'checkbox' && this.validateCheckbox())
24761 this.markInvalid();
24765 validateRadio : function()
24767 if(this.getVisibilityEl().hasClass('hidden')){
24771 if(this.allowBlank){
24777 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24778 if(!e.dom.checked){
24790 validateCheckbox : function()
24793 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24794 //return (this.getValue() == this.inputValue) ? true : false;
24797 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24805 for(var i in group){
24806 if(group[i].el.isVisible(true)){
24814 for(var i in group){
24819 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24826 * Mark this field as valid
24828 markValid : function()
24832 this.fireEvent('valid', this);
24834 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24837 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24844 if(this.inputType == 'radio'){
24845 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24846 var fg = e.findParent('.form-group', false, true);
24847 if (Roo.bootstrap.version == 3) {
24848 fg.removeClass([_this.invalidClass, _this.validClass]);
24849 fg.addClass(_this.validClass);
24851 fg.removeClass(['is-valid', 'is-invalid']);
24852 fg.addClass('is-valid');
24860 var fg = this.el.findParent('.form-group', false, true);
24861 if (Roo.bootstrap.version == 3) {
24862 fg.removeClass([this.invalidClass, this.validClass]);
24863 fg.addClass(this.validClass);
24865 fg.removeClass(['is-valid', 'is-invalid']);
24866 fg.addClass('is-valid');
24871 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24877 for(var i in group){
24878 var fg = group[i].el.findParent('.form-group', false, true);
24879 if (Roo.bootstrap.version == 3) {
24880 fg.removeClass([this.invalidClass, this.validClass]);
24881 fg.addClass(this.validClass);
24883 fg.removeClass(['is-valid', 'is-invalid']);
24884 fg.addClass('is-valid');
24890 * Mark this field as invalid
24891 * @param {String} msg The validation message
24893 markInvalid : function(msg)
24895 if(this.allowBlank){
24901 this.fireEvent('invalid', this, msg);
24903 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24906 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24910 label.markInvalid();
24913 if(this.inputType == 'radio'){
24915 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24916 var fg = e.findParent('.form-group', false, true);
24917 if (Roo.bootstrap.version == 3) {
24918 fg.removeClass([_this.invalidClass, _this.validClass]);
24919 fg.addClass(_this.invalidClass);
24921 fg.removeClass(['is-invalid', 'is-valid']);
24922 fg.addClass('is-invalid');
24930 var fg = this.el.findParent('.form-group', false, true);
24931 if (Roo.bootstrap.version == 3) {
24932 fg.removeClass([_this.invalidClass, _this.validClass]);
24933 fg.addClass(_this.invalidClass);
24935 fg.removeClass(['is-invalid', 'is-valid']);
24936 fg.addClass('is-invalid');
24941 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24947 for(var i in group){
24948 var fg = group[i].el.findParent('.form-group', false, true);
24949 if (Roo.bootstrap.version == 3) {
24950 fg.removeClass([_this.invalidClass, _this.validClass]);
24951 fg.addClass(_this.invalidClass);
24953 fg.removeClass(['is-invalid', 'is-valid']);
24954 fg.addClass('is-invalid');
24960 clearInvalid : function()
24962 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24964 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24966 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24968 if (label && label.iconEl) {
24969 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24970 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24974 disable : function()
24976 if(this.inputType != 'radio'){
24977 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24984 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24985 _this.getActionEl().addClass(this.disabledClass);
24986 e.dom.disabled = true;
24990 this.disabled = true;
24991 this.fireEvent("disable", this);
24995 enable : function()
24997 if(this.inputType != 'radio'){
24998 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25005 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25006 _this.getActionEl().removeClass(this.disabledClass);
25007 e.dom.disabled = false;
25011 this.disabled = false;
25012 this.fireEvent("enable", this);
25016 setBoxLabel : function(v)
25021 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25027 Roo.apply(Roo.bootstrap.CheckBox, {
25032 * register a CheckBox Group
25033 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25035 register : function(checkbox)
25037 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25038 this.groups[checkbox.groupId] = {};
25041 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25045 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25049 * fetch a CheckBox Group based on the group ID
25050 * @param {string} the group ID
25051 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25053 get: function(groupId) {
25054 if (typeof(this.groups[groupId]) == 'undefined') {
25058 return this.groups[groupId] ;
25071 * @class Roo.bootstrap.Radio
25072 * @extends Roo.bootstrap.Component
25073 * Bootstrap Radio class
25074 * @cfg {String} boxLabel - the label associated
25075 * @cfg {String} value - the value of radio
25078 * Create a new Radio
25079 * @param {Object} config The config object
25081 Roo.bootstrap.Radio = function(config){
25082 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25086 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25092 getAutoCreate : function()
25096 cls : 'form-group radio',
25101 html : this.boxLabel
25109 initEvents : function()
25111 this.parent().register(this);
25113 this.el.on('click', this.onClick, this);
25117 onClick : function(e)
25119 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25120 this.setChecked(true);
25124 setChecked : function(state, suppressEvent)
25126 this.parent().setValue(this.value, suppressEvent);
25130 setBoxLabel : function(v)
25135 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25150 * @class Roo.bootstrap.SecurePass
25151 * @extends Roo.bootstrap.Input
25152 * Bootstrap SecurePass class
25156 * Create a new SecurePass
25157 * @param {Object} config The config object
25160 Roo.bootstrap.SecurePass = function (config) {
25161 // these go here, so the translation tool can replace them..
25163 PwdEmpty: "Please type a password, and then retype it to confirm.",
25164 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25165 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25166 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25167 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25168 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25169 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25170 TooWeak: "Your password is Too Weak."
25172 this.meterLabel = "Password strength:";
25173 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25174 this.meterClass = [
25175 "roo-password-meter-tooweak",
25176 "roo-password-meter-weak",
25177 "roo-password-meter-medium",
25178 "roo-password-meter-strong",
25179 "roo-password-meter-grey"
25184 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25187 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25189 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25191 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25192 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25193 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25194 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25195 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25196 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25197 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25207 * @cfg {String/Object} Label for the strength meter (defaults to
25208 * 'Password strength:')
25213 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25214 * ['Weak', 'Medium', 'Strong'])
25217 pwdStrengths: false,
25230 initEvents: function ()
25232 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25234 if (this.el.is('input[type=password]') && Roo.isSafari) {
25235 this.el.on('keydown', this.SafariOnKeyDown, this);
25238 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25241 onRender: function (ct, position)
25243 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25244 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25245 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25247 this.trigger.createChild({
25252 cls: 'roo-password-meter-grey col-xs-12',
25255 //width: this.meterWidth + 'px'
25259 cls: 'roo-password-meter-text'
25265 if (this.hideTrigger) {
25266 this.trigger.setDisplayed(false);
25268 this.setSize(this.width || '', this.height || '');
25271 onDestroy: function ()
25273 if (this.trigger) {
25274 this.trigger.removeAllListeners();
25275 this.trigger.remove();
25278 this.wrap.remove();
25280 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25283 checkStrength: function ()
25285 var pwd = this.inputEl().getValue();
25286 if (pwd == this._lastPwd) {
25291 if (this.ClientSideStrongPassword(pwd)) {
25293 } else if (this.ClientSideMediumPassword(pwd)) {
25295 } else if (this.ClientSideWeakPassword(pwd)) {
25301 Roo.log('strength1: ' + strength);
25303 //var pm = this.trigger.child('div/div/div').dom;
25304 var pm = this.trigger.child('div/div');
25305 pm.removeClass(this.meterClass);
25306 pm.addClass(this.meterClass[strength]);
25309 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25311 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25313 this._lastPwd = pwd;
25317 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25319 this._lastPwd = '';
25321 var pm = this.trigger.child('div/div');
25322 pm.removeClass(this.meterClass);
25323 pm.addClass('roo-password-meter-grey');
25326 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25329 this.inputEl().dom.type='password';
25332 validateValue: function (value)
25334 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25337 if (value.length == 0) {
25338 if (this.allowBlank) {
25339 this.clearInvalid();
25343 this.markInvalid(this.errors.PwdEmpty);
25344 this.errorMsg = this.errors.PwdEmpty;
25352 if (!value.match(/[\x21-\x7e]+/)) {
25353 this.markInvalid(this.errors.PwdBadChar);
25354 this.errorMsg = this.errors.PwdBadChar;
25357 if (value.length < 6) {
25358 this.markInvalid(this.errors.PwdShort);
25359 this.errorMsg = this.errors.PwdShort;
25362 if (value.length > 16) {
25363 this.markInvalid(this.errors.PwdLong);
25364 this.errorMsg = this.errors.PwdLong;
25368 if (this.ClientSideStrongPassword(value)) {
25370 } else if (this.ClientSideMediumPassword(value)) {
25372 } else if (this.ClientSideWeakPassword(value)) {
25379 if (strength < 2) {
25380 //this.markInvalid(this.errors.TooWeak);
25381 this.errorMsg = this.errors.TooWeak;
25386 console.log('strength2: ' + strength);
25388 //var pm = this.trigger.child('div/div/div').dom;
25390 var pm = this.trigger.child('div/div');
25391 pm.removeClass(this.meterClass);
25392 pm.addClass(this.meterClass[strength]);
25394 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25396 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25398 this.errorMsg = '';
25402 CharacterSetChecks: function (type)
25405 this.fResult = false;
25408 isctype: function (character, type)
25411 case this.kCapitalLetter:
25412 if (character >= 'A' && character <= 'Z') {
25417 case this.kSmallLetter:
25418 if (character >= 'a' && character <= 'z') {
25424 if (character >= '0' && character <= '9') {
25429 case this.kPunctuation:
25430 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25441 IsLongEnough: function (pwd, size)
25443 return !(pwd == null || isNaN(size) || pwd.length < size);
25446 SpansEnoughCharacterSets: function (word, nb)
25448 if (!this.IsLongEnough(word, nb))
25453 var characterSetChecks = new Array(
25454 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25455 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25458 for (var index = 0; index < word.length; ++index) {
25459 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25460 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25461 characterSetChecks[nCharSet].fResult = true;
25468 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25469 if (characterSetChecks[nCharSet].fResult) {
25474 if (nCharSets < nb) {
25480 ClientSideStrongPassword: function (pwd)
25482 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25485 ClientSideMediumPassword: function (pwd)
25487 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25490 ClientSideWeakPassword: function (pwd)
25492 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25495 })//<script type="text/javascript">
25498 * Based Ext JS Library 1.1.1
25499 * Copyright(c) 2006-2007, Ext JS, LLC.
25505 * @class Roo.HtmlEditorCore
25506 * @extends Roo.Component
25507 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25509 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25512 Roo.HtmlEditorCore = function(config){
25515 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25520 * @event initialize
25521 * Fires when the editor is fully initialized (including the iframe)
25522 * @param {Roo.HtmlEditorCore} this
25527 * Fires when the editor is first receives the focus. Any insertion must wait
25528 * until after this event.
25529 * @param {Roo.HtmlEditorCore} this
25533 * @event beforesync
25534 * Fires before the textarea is updated with content from the editor iframe. Return false
25535 * to cancel the sync.
25536 * @param {Roo.HtmlEditorCore} this
25537 * @param {String} html
25541 * @event beforepush
25542 * Fires before the iframe editor is updated with content from the textarea. Return false
25543 * to cancel the push.
25544 * @param {Roo.HtmlEditorCore} this
25545 * @param {String} html
25550 * Fires when the textarea is updated with content from the editor iframe.
25551 * @param {Roo.HtmlEditorCore} this
25552 * @param {String} html
25557 * Fires when the iframe editor is updated with content from the textarea.
25558 * @param {Roo.HtmlEditorCore} this
25559 * @param {String} html
25564 * @event editorevent
25565 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25566 * @param {Roo.HtmlEditorCore} this
25572 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25574 // defaults : white / black...
25575 this.applyBlacklists();
25582 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25586 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25592 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25597 * @cfg {Number} height (in pixels)
25601 * @cfg {Number} width (in pixels)
25606 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25609 stylesheets: false,
25614 // private properties
25615 validationEvent : false,
25617 initialized : false,
25619 sourceEditMode : false,
25620 onFocus : Roo.emptyFn,
25622 hideMode:'offsets',
25626 // blacklist + whitelisted elements..
25633 * Protected method that will not generally be called directly. It
25634 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25635 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25637 getDocMarkup : function(){
25641 // inherit styels from page...??
25642 if (this.stylesheets === false) {
25644 Roo.get(document.head).select('style').each(function(node) {
25645 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25648 Roo.get(document.head).select('link').each(function(node) {
25649 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25652 } else if (!this.stylesheets.length) {
25654 st = '<style type="text/css">' +
25655 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25658 for (var i in this.stylesheets) {
25659 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25664 st += '<style type="text/css">' +
25665 'IMG { cursor: pointer } ' +
25668 var cls = 'roo-htmleditor-body';
25670 if(this.bodyCls.length){
25671 cls += ' ' + this.bodyCls;
25674 return '<html><head>' + st +
25675 //<style type="text/css">' +
25676 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25678 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25682 onRender : function(ct, position)
25685 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25686 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25689 this.el.dom.style.border = '0 none';
25690 this.el.dom.setAttribute('tabIndex', -1);
25691 this.el.addClass('x-hidden hide');
25695 if(Roo.isIE){ // fix IE 1px bogus margin
25696 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25700 this.frameId = Roo.id();
25704 var iframe = this.owner.wrap.createChild({
25706 cls: 'form-control', // bootstrap..
25708 name: this.frameId,
25709 frameBorder : 'no',
25710 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25715 this.iframe = iframe.dom;
25717 this.assignDocWin();
25719 this.doc.designMode = 'on';
25722 this.doc.write(this.getDocMarkup());
25726 var task = { // must defer to wait for browser to be ready
25728 //console.log("run task?" + this.doc.readyState);
25729 this.assignDocWin();
25730 if(this.doc.body || this.doc.readyState == 'complete'){
25732 this.doc.designMode="on";
25736 Roo.TaskMgr.stop(task);
25737 this.initEditor.defer(10, this);
25744 Roo.TaskMgr.start(task);
25749 onResize : function(w, h)
25751 Roo.log('resize: ' +w + ',' + h );
25752 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25756 if(typeof w == 'number'){
25758 this.iframe.style.width = w + 'px';
25760 if(typeof h == 'number'){
25762 this.iframe.style.height = h + 'px';
25764 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25771 * Toggles the editor between standard and source edit mode.
25772 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25774 toggleSourceEdit : function(sourceEditMode){
25776 this.sourceEditMode = sourceEditMode === true;
25778 if(this.sourceEditMode){
25780 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25783 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25784 //this.iframe.className = '';
25787 //this.setSize(this.owner.wrap.getSize());
25788 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25795 * Protected method that will not generally be called directly. If you need/want
25796 * custom HTML cleanup, this is the method you should override.
25797 * @param {String} html The HTML to be cleaned
25798 * return {String} The cleaned HTML
25800 cleanHtml : function(html){
25801 html = String(html);
25802 if(html.length > 5){
25803 if(Roo.isSafari){ // strip safari nonsense
25804 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25807 if(html == ' '){
25814 * HTML Editor -> Textarea
25815 * Protected method that will not generally be called directly. Syncs the contents
25816 * of the editor iframe with the textarea.
25818 syncValue : function(){
25819 if(this.initialized){
25820 var bd = (this.doc.body || this.doc.documentElement);
25821 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25822 var html = bd.innerHTML;
25824 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25825 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25827 html = '<div style="'+m[0]+'">' + html + '</div>';
25830 html = this.cleanHtml(html);
25831 // fix up the special chars.. normaly like back quotes in word...
25832 // however we do not want to do this with chinese..
25833 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25835 var cc = match.charCodeAt();
25837 // Get the character value, handling surrogate pairs
25838 if (match.length == 2) {
25839 // It's a surrogate pair, calculate the Unicode code point
25840 var high = match.charCodeAt(0) - 0xD800;
25841 var low = match.charCodeAt(1) - 0xDC00;
25842 cc = (high * 0x400) + low + 0x10000;
25844 (cc >= 0x4E00 && cc < 0xA000 ) ||
25845 (cc >= 0x3400 && cc < 0x4E00 ) ||
25846 (cc >= 0xf900 && cc < 0xfb00 )
25851 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25852 return "&#" + cc + ";";
25859 if(this.owner.fireEvent('beforesync', this, html) !== false){
25860 this.el.dom.value = html;
25861 this.owner.fireEvent('sync', this, html);
25867 * Protected method that will not generally be called directly. Pushes the value of the textarea
25868 * into the iframe editor.
25870 pushValue : function(){
25871 if(this.initialized){
25872 var v = this.el.dom.value.trim();
25874 // if(v.length < 1){
25878 if(this.owner.fireEvent('beforepush', this, v) !== false){
25879 var d = (this.doc.body || this.doc.documentElement);
25881 this.cleanUpPaste();
25882 this.el.dom.value = d.innerHTML;
25883 this.owner.fireEvent('push', this, v);
25889 deferFocus : function(){
25890 this.focus.defer(10, this);
25894 focus : function(){
25895 if(this.win && !this.sourceEditMode){
25902 assignDocWin: function()
25904 var iframe = this.iframe;
25907 this.doc = iframe.contentWindow.document;
25908 this.win = iframe.contentWindow;
25910 // if (!Roo.get(this.frameId)) {
25913 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25914 // this.win = Roo.get(this.frameId).dom.contentWindow;
25916 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25920 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25921 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25926 initEditor : function(){
25927 //console.log("INIT EDITOR");
25928 this.assignDocWin();
25932 this.doc.designMode="on";
25934 this.doc.write(this.getDocMarkup());
25937 var dbody = (this.doc.body || this.doc.documentElement);
25938 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25939 // this copies styles from the containing element into thsi one..
25940 // not sure why we need all of this..
25941 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25943 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25944 //ss['background-attachment'] = 'fixed'; // w3c
25945 dbody.bgProperties = 'fixed'; // ie
25946 //Roo.DomHelper.applyStyles(dbody, ss);
25947 Roo.EventManager.on(this.doc, {
25948 //'mousedown': this.onEditorEvent,
25949 'mouseup': this.onEditorEvent,
25950 'dblclick': this.onEditorEvent,
25951 'click': this.onEditorEvent,
25952 'keyup': this.onEditorEvent,
25957 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25959 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25960 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25962 this.initialized = true;
25964 this.owner.fireEvent('initialize', this);
25969 onDestroy : function(){
25975 //for (var i =0; i < this.toolbars.length;i++) {
25976 // // fixme - ask toolbars for heights?
25977 // this.toolbars[i].onDestroy();
25980 //this.wrap.dom.innerHTML = '';
25981 //this.wrap.remove();
25986 onFirstFocus : function(){
25988 this.assignDocWin();
25991 this.activated = true;
25994 if(Roo.isGecko){ // prevent silly gecko errors
25996 var s = this.win.getSelection();
25997 if(!s.focusNode || s.focusNode.nodeType != 3){
25998 var r = s.getRangeAt(0);
25999 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26004 this.execCmd('useCSS', true);
26005 this.execCmd('styleWithCSS', false);
26008 this.owner.fireEvent('activate', this);
26012 adjustFont: function(btn){
26013 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26014 //if(Roo.isSafari){ // safari
26017 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26018 if(Roo.isSafari){ // safari
26019 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26020 v = (v < 10) ? 10 : v;
26021 v = (v > 48) ? 48 : v;
26022 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26027 v = Math.max(1, v+adjust);
26029 this.execCmd('FontSize', v );
26032 onEditorEvent : function(e)
26034 this.owner.fireEvent('editorevent', this, e);
26035 // this.updateToolbar();
26036 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26039 insertTag : function(tg)
26041 // could be a bit smarter... -> wrap the current selected tRoo..
26042 if (tg.toLowerCase() == 'span' ||
26043 tg.toLowerCase() == 'code' ||
26044 tg.toLowerCase() == 'sup' ||
26045 tg.toLowerCase() == 'sub'
26048 range = this.createRange(this.getSelection());
26049 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26050 wrappingNode.appendChild(range.extractContents());
26051 range.insertNode(wrappingNode);
26058 this.execCmd("formatblock", tg);
26062 insertText : function(txt)
26066 var range = this.createRange();
26067 range.deleteContents();
26068 //alert(Sender.getAttribute('label'));
26070 range.insertNode(this.doc.createTextNode(txt));
26076 * Executes a Midas editor command on the editor document and performs necessary focus and
26077 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26078 * @param {String} cmd The Midas command
26079 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26081 relayCmd : function(cmd, value){
26083 this.execCmd(cmd, value);
26084 this.owner.fireEvent('editorevent', this);
26085 //this.updateToolbar();
26086 this.owner.deferFocus();
26090 * Executes a Midas editor command directly on the editor document.
26091 * For visual commands, you should use {@link #relayCmd} instead.
26092 * <b>This should only be called after the editor is initialized.</b>
26093 * @param {String} cmd The Midas command
26094 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26096 execCmd : function(cmd, value){
26097 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26104 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26106 * @param {String} text | dom node..
26108 insertAtCursor : function(text)
26111 if(!this.activated){
26117 var r = this.doc.selection.createRange();
26128 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26132 // from jquery ui (MIT licenced)
26134 var win = this.win;
26136 if (win.getSelection && win.getSelection().getRangeAt) {
26137 range = win.getSelection().getRangeAt(0);
26138 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26139 range.insertNode(node);
26140 } else if (win.document.selection && win.document.selection.createRange) {
26141 // no firefox support
26142 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26143 win.document.selection.createRange().pasteHTML(txt);
26145 // no firefox support
26146 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26147 this.execCmd('InsertHTML', txt);
26156 mozKeyPress : function(e){
26158 var c = e.getCharCode(), cmd;
26161 c = String.fromCharCode(c).toLowerCase();
26175 this.cleanUpPaste.defer(100, this);
26183 e.preventDefault();
26191 fixKeys : function(){ // load time branching for fastest keydown performance
26193 return function(e){
26194 var k = e.getKey(), r;
26197 r = this.doc.selection.createRange();
26200 r.pasteHTML('    ');
26207 r = this.doc.selection.createRange();
26209 var target = r.parentElement();
26210 if(!target || target.tagName.toLowerCase() != 'li'){
26212 r.pasteHTML('<br />');
26218 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26219 this.cleanUpPaste.defer(100, this);
26225 }else if(Roo.isOpera){
26226 return function(e){
26227 var k = e.getKey();
26231 this.execCmd('InsertHTML','    ');
26234 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26235 this.cleanUpPaste.defer(100, this);
26240 }else if(Roo.isSafari){
26241 return function(e){
26242 var k = e.getKey();
26246 this.execCmd('InsertText','\t');
26250 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26251 this.cleanUpPaste.defer(100, this);
26259 getAllAncestors: function()
26261 var p = this.getSelectedNode();
26264 a.push(p); // push blank onto stack..
26265 p = this.getParentElement();
26269 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26273 a.push(this.doc.body);
26277 lastSelNode : false,
26280 getSelection : function()
26282 this.assignDocWin();
26283 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26286 getSelectedNode: function()
26288 // this may only work on Gecko!!!
26290 // should we cache this!!!!
26295 var range = this.createRange(this.getSelection()).cloneRange();
26298 var parent = range.parentElement();
26300 var testRange = range.duplicate();
26301 testRange.moveToElementText(parent);
26302 if (testRange.inRange(range)) {
26305 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26308 parent = parent.parentElement;
26313 // is ancestor a text element.
26314 var ac = range.commonAncestorContainer;
26315 if (ac.nodeType == 3) {
26316 ac = ac.parentNode;
26319 var ar = ac.childNodes;
26322 var other_nodes = [];
26323 var has_other_nodes = false;
26324 for (var i=0;i<ar.length;i++) {
26325 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26328 // fullly contained node.
26330 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26335 // probably selected..
26336 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26337 other_nodes.push(ar[i]);
26341 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26346 has_other_nodes = true;
26348 if (!nodes.length && other_nodes.length) {
26349 nodes= other_nodes;
26351 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26357 createRange: function(sel)
26359 // this has strange effects when using with
26360 // top toolbar - not sure if it's a great idea.
26361 //this.editor.contentWindow.focus();
26362 if (typeof sel != "undefined") {
26364 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26366 return this.doc.createRange();
26369 return this.doc.createRange();
26372 getParentElement: function()
26375 this.assignDocWin();
26376 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26378 var range = this.createRange(sel);
26381 var p = range.commonAncestorContainer;
26382 while (p.nodeType == 3) { // text node
26393 * Range intersection.. the hard stuff...
26397 * [ -- selected range --- ]
26401 * if end is before start or hits it. fail.
26402 * if start is after end or hits it fail.
26404 * if either hits (but other is outside. - then it's not
26410 // @see http://www.thismuchiknow.co.uk/?p=64.
26411 rangeIntersectsNode : function(range, node)
26413 var nodeRange = node.ownerDocument.createRange();
26415 nodeRange.selectNode(node);
26417 nodeRange.selectNodeContents(node);
26420 var rangeStartRange = range.cloneRange();
26421 rangeStartRange.collapse(true);
26423 var rangeEndRange = range.cloneRange();
26424 rangeEndRange.collapse(false);
26426 var nodeStartRange = nodeRange.cloneRange();
26427 nodeStartRange.collapse(true);
26429 var nodeEndRange = nodeRange.cloneRange();
26430 nodeEndRange.collapse(false);
26432 return rangeStartRange.compareBoundaryPoints(
26433 Range.START_TO_START, nodeEndRange) == -1 &&
26434 rangeEndRange.compareBoundaryPoints(
26435 Range.START_TO_START, nodeStartRange) == 1;
26439 rangeCompareNode : function(range, node)
26441 var nodeRange = node.ownerDocument.createRange();
26443 nodeRange.selectNode(node);
26445 nodeRange.selectNodeContents(node);
26449 range.collapse(true);
26451 nodeRange.collapse(true);
26453 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26454 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26456 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26458 var nodeIsBefore = ss == 1;
26459 var nodeIsAfter = ee == -1;
26461 if (nodeIsBefore && nodeIsAfter) {
26464 if (!nodeIsBefore && nodeIsAfter) {
26465 return 1; //right trailed.
26468 if (nodeIsBefore && !nodeIsAfter) {
26469 return 2; // left trailed.
26475 // private? - in a new class?
26476 cleanUpPaste : function()
26478 // cleans up the whole document..
26479 Roo.log('cleanuppaste');
26481 this.cleanUpChildren(this.doc.body);
26482 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26483 if (clean != this.doc.body.innerHTML) {
26484 this.doc.body.innerHTML = clean;
26489 cleanWordChars : function(input) {// change the chars to hex code
26490 var he = Roo.HtmlEditorCore;
26492 var output = input;
26493 Roo.each(he.swapCodes, function(sw) {
26494 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26496 output = output.replace(swapper, sw[1]);
26503 cleanUpChildren : function (n)
26505 if (!n.childNodes.length) {
26508 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26509 this.cleanUpChild(n.childNodes[i]);
26516 cleanUpChild : function (node)
26519 //console.log(node);
26520 if (node.nodeName == "#text") {
26521 // clean up silly Windows -- stuff?
26524 if (node.nodeName == "#comment") {
26525 node.parentNode.removeChild(node);
26526 // clean up silly Windows -- stuff?
26529 var lcname = node.tagName.toLowerCase();
26530 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26531 // whitelist of tags..
26533 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26535 node.parentNode.removeChild(node);
26540 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26542 // spans with no attributes - just remove them..
26543 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26544 remove_keep_children = true;
26547 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26548 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26550 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26551 // remove_keep_children = true;
26554 if (remove_keep_children) {
26555 this.cleanUpChildren(node);
26556 // inserts everything just before this node...
26557 while (node.childNodes.length) {
26558 var cn = node.childNodes[0];
26559 node.removeChild(cn);
26560 node.parentNode.insertBefore(cn, node);
26562 node.parentNode.removeChild(node);
26566 if (!node.attributes || !node.attributes.length) {
26571 this.cleanUpChildren(node);
26575 function cleanAttr(n,v)
26578 if (v.match(/^\./) || v.match(/^\//)) {
26581 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26584 if (v.match(/^#/)) {
26587 if (v.match(/^\{/)) { // allow template editing.
26590 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26591 node.removeAttribute(n);
26595 var cwhite = this.cwhite;
26596 var cblack = this.cblack;
26598 function cleanStyle(n,v)
26600 if (v.match(/expression/)) { //XSS?? should we even bother..
26601 node.removeAttribute(n);
26605 var parts = v.split(/;/);
26608 Roo.each(parts, function(p) {
26609 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26613 var l = p.split(':').shift().replace(/\s+/g,'');
26614 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26616 if ( cwhite.length && cblack.indexOf(l) > -1) {
26617 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26618 //node.removeAttribute(n);
26622 // only allow 'c whitelisted system attributes'
26623 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26624 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26625 //node.removeAttribute(n);
26635 if (clean.length) {
26636 node.setAttribute(n, clean.join(';'));
26638 node.removeAttribute(n);
26644 for (var i = node.attributes.length-1; i > -1 ; i--) {
26645 var a = node.attributes[i];
26648 if (a.name.toLowerCase().substr(0,2)=='on') {
26649 node.removeAttribute(a.name);
26652 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26653 node.removeAttribute(a.name);
26656 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26657 cleanAttr(a.name,a.value); // fixme..
26660 if (a.name == 'style') {
26661 cleanStyle(a.name,a.value);
26664 /// clean up MS crap..
26665 // tecnically this should be a list of valid class'es..
26668 if (a.name == 'class') {
26669 if (a.value.match(/^Mso/)) {
26670 node.removeAttribute('class');
26673 if (a.value.match(/^body$/)) {
26674 node.removeAttribute('class');
26685 this.cleanUpChildren(node);
26691 * Clean up MS wordisms...
26693 cleanWord : function(node)
26696 this.cleanWord(this.doc.body);
26701 node.nodeName == 'SPAN' &&
26702 !node.hasAttributes() &&
26703 node.childNodes.length == 1 &&
26704 node.firstChild.nodeName == "#text"
26706 var textNode = node.firstChild;
26707 node.removeChild(textNode);
26708 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26709 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26711 node.parentNode.insertBefore(textNode, node);
26712 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26713 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26715 node.parentNode.removeChild(node);
26718 if (node.nodeName == "#text") {
26719 // clean up silly Windows -- stuff?
26722 if (node.nodeName == "#comment") {
26723 node.parentNode.removeChild(node);
26724 // clean up silly Windows -- stuff?
26728 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26729 node.parentNode.removeChild(node);
26732 //Roo.log(node.tagName);
26733 // remove - but keep children..
26734 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26735 //Roo.log('-- removed');
26736 while (node.childNodes.length) {
26737 var cn = node.childNodes[0];
26738 node.removeChild(cn);
26739 node.parentNode.insertBefore(cn, node);
26740 // move node to parent - and clean it..
26741 this.cleanWord(cn);
26743 node.parentNode.removeChild(node);
26744 /// no need to iterate chidlren = it's got none..
26745 //this.iterateChildren(node, this.cleanWord);
26749 if (node.className.length) {
26751 var cn = node.className.split(/\W+/);
26753 Roo.each(cn, function(cls) {
26754 if (cls.match(/Mso[a-zA-Z]+/)) {
26759 node.className = cna.length ? cna.join(' ') : '';
26761 node.removeAttribute("class");
26765 if (node.hasAttribute("lang")) {
26766 node.removeAttribute("lang");
26769 if (node.hasAttribute("style")) {
26771 var styles = node.getAttribute("style").split(";");
26773 Roo.each(styles, function(s) {
26774 if (!s.match(/:/)) {
26777 var kv = s.split(":");
26778 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26781 // what ever is left... we allow.
26784 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26785 if (!nstyle.length) {
26786 node.removeAttribute('style');
26789 this.iterateChildren(node, this.cleanWord);
26795 * iterateChildren of a Node, calling fn each time, using this as the scole..
26796 * @param {DomNode} node node to iterate children of.
26797 * @param {Function} fn method of this class to call on each item.
26799 iterateChildren : function(node, fn)
26801 if (!node.childNodes.length) {
26804 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26805 fn.call(this, node.childNodes[i])
26811 * cleanTableWidths.
26813 * Quite often pasting from word etc.. results in tables with column and widths.
26814 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26817 cleanTableWidths : function(node)
26822 this.cleanTableWidths(this.doc.body);
26827 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26830 Roo.log(node.tagName);
26831 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26832 this.iterateChildren(node, this.cleanTableWidths);
26835 if (node.hasAttribute('width')) {
26836 node.removeAttribute('width');
26840 if (node.hasAttribute("style")) {
26843 var styles = node.getAttribute("style").split(";");
26845 Roo.each(styles, function(s) {
26846 if (!s.match(/:/)) {
26849 var kv = s.split(":");
26850 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26853 // what ever is left... we allow.
26856 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26857 if (!nstyle.length) {
26858 node.removeAttribute('style');
26862 this.iterateChildren(node, this.cleanTableWidths);
26870 domToHTML : function(currentElement, depth, nopadtext) {
26872 depth = depth || 0;
26873 nopadtext = nopadtext || false;
26875 if (!currentElement) {
26876 return this.domToHTML(this.doc.body);
26879 //Roo.log(currentElement);
26881 var allText = false;
26882 var nodeName = currentElement.nodeName;
26883 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26885 if (nodeName == '#text') {
26887 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26892 if (nodeName != 'BODY') {
26895 // Prints the node tagName, such as <A>, <IMG>, etc
26898 for(i = 0; i < currentElement.attributes.length;i++) {
26900 var aname = currentElement.attributes.item(i).name;
26901 if (!currentElement.attributes.item(i).value.length) {
26904 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26907 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26916 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26919 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26924 // Traverse the tree
26926 var currentElementChild = currentElement.childNodes.item(i);
26927 var allText = true;
26928 var innerHTML = '';
26930 while (currentElementChild) {
26931 // Formatting code (indent the tree so it looks nice on the screen)
26932 var nopad = nopadtext;
26933 if (lastnode == 'SPAN') {
26937 if (currentElementChild.nodeName == '#text') {
26938 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26939 toadd = nopadtext ? toadd : toadd.trim();
26940 if (!nopad && toadd.length > 80) {
26941 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26943 innerHTML += toadd;
26946 currentElementChild = currentElement.childNodes.item(i);
26952 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26954 // Recursively traverse the tree structure of the child node
26955 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26956 lastnode = currentElementChild.nodeName;
26958 currentElementChild=currentElement.childNodes.item(i);
26964 // The remaining code is mostly for formatting the tree
26965 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26970 ret+= "</"+tagName+">";
26976 applyBlacklists : function()
26978 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26979 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26983 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26984 if (b.indexOf(tag) > -1) {
26987 this.white.push(tag);
26991 Roo.each(w, function(tag) {
26992 if (b.indexOf(tag) > -1) {
26995 if (this.white.indexOf(tag) > -1) {
26998 this.white.push(tag);
27003 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27004 if (w.indexOf(tag) > -1) {
27007 this.black.push(tag);
27011 Roo.each(b, function(tag) {
27012 if (w.indexOf(tag) > -1) {
27015 if (this.black.indexOf(tag) > -1) {
27018 this.black.push(tag);
27023 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27024 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27028 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27029 if (b.indexOf(tag) > -1) {
27032 this.cwhite.push(tag);
27036 Roo.each(w, function(tag) {
27037 if (b.indexOf(tag) > -1) {
27040 if (this.cwhite.indexOf(tag) > -1) {
27043 this.cwhite.push(tag);
27048 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27049 if (w.indexOf(tag) > -1) {
27052 this.cblack.push(tag);
27056 Roo.each(b, function(tag) {
27057 if (w.indexOf(tag) > -1) {
27060 if (this.cblack.indexOf(tag) > -1) {
27063 this.cblack.push(tag);
27068 setStylesheets : function(stylesheets)
27070 if(typeof(stylesheets) == 'string'){
27071 Roo.get(this.iframe.contentDocument.head).createChild({
27073 rel : 'stylesheet',
27082 Roo.each(stylesheets, function(s) {
27087 Roo.get(_this.iframe.contentDocument.head).createChild({
27089 rel : 'stylesheet',
27098 removeStylesheets : function()
27102 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27107 setStyle : function(style)
27109 Roo.get(this.iframe.contentDocument.head).createChild({
27118 // hide stuff that is not compatible
27132 * @event specialkey
27136 * @cfg {String} fieldClass @hide
27139 * @cfg {String} focusClass @hide
27142 * @cfg {String} autoCreate @hide
27145 * @cfg {String} inputType @hide
27148 * @cfg {String} invalidClass @hide
27151 * @cfg {String} invalidText @hide
27154 * @cfg {String} msgFx @hide
27157 * @cfg {String} validateOnBlur @hide
27161 Roo.HtmlEditorCore.white = [
27162 'area', 'br', 'img', 'input', 'hr', 'wbr',
27164 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27165 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27166 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27167 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27168 'table', 'ul', 'xmp',
27170 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27173 'dir', 'menu', 'ol', 'ul', 'dl',
27179 Roo.HtmlEditorCore.black = [
27180 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27182 'base', 'basefont', 'bgsound', 'blink', 'body',
27183 'frame', 'frameset', 'head', 'html', 'ilayer',
27184 'iframe', 'layer', 'link', 'meta', 'object',
27185 'script', 'style' ,'title', 'xml' // clean later..
27187 Roo.HtmlEditorCore.clean = [
27188 'script', 'style', 'title', 'xml'
27190 Roo.HtmlEditorCore.remove = [
27195 Roo.HtmlEditorCore.ablack = [
27199 Roo.HtmlEditorCore.aclean = [
27200 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27204 Roo.HtmlEditorCore.pwhite= [
27205 'http', 'https', 'mailto'
27208 // white listed style attributes.
27209 Roo.HtmlEditorCore.cwhite= [
27210 // 'text-align', /// default is to allow most things..
27216 // black listed style attributes.
27217 Roo.HtmlEditorCore.cblack= [
27218 // 'font-size' -- this can be set by the project
27222 Roo.HtmlEditorCore.swapCodes =[
27223 [ 8211, "–" ],
27224 [ 8212, "—" ],
27241 * @class Roo.bootstrap.HtmlEditor
27242 * @extends Roo.bootstrap.TextArea
27243 * Bootstrap HtmlEditor class
27246 * Create a new HtmlEditor
27247 * @param {Object} config The config object
27250 Roo.bootstrap.HtmlEditor = function(config){
27251 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27252 if (!this.toolbars) {
27253 this.toolbars = [];
27256 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27259 * @event initialize
27260 * Fires when the editor is fully initialized (including the iframe)
27261 * @param {HtmlEditor} this
27266 * Fires when the editor is first receives the focus. Any insertion must wait
27267 * until after this event.
27268 * @param {HtmlEditor} this
27272 * @event beforesync
27273 * Fires before the textarea is updated with content from the editor iframe. Return false
27274 * to cancel the sync.
27275 * @param {HtmlEditor} this
27276 * @param {String} html
27280 * @event beforepush
27281 * Fires before the iframe editor is updated with content from the textarea. Return false
27282 * to cancel the push.
27283 * @param {HtmlEditor} this
27284 * @param {String} html
27289 * Fires when the textarea is updated with content from the editor iframe.
27290 * @param {HtmlEditor} this
27291 * @param {String} html
27296 * Fires when the iframe editor is updated with content from the textarea.
27297 * @param {HtmlEditor} this
27298 * @param {String} html
27302 * @event editmodechange
27303 * Fires when the editor switches edit modes
27304 * @param {HtmlEditor} this
27305 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27307 editmodechange: true,
27309 * @event editorevent
27310 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27311 * @param {HtmlEditor} this
27315 * @event firstfocus
27316 * Fires when on first focus - needed by toolbars..
27317 * @param {HtmlEditor} this
27322 * Auto save the htmlEditor value as a file into Events
27323 * @param {HtmlEditor} this
27327 * @event savedpreview
27328 * preview the saved version of htmlEditor
27329 * @param {HtmlEditor} this
27336 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27340 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27345 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27350 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27355 * @cfg {Number} height (in pixels)
27359 * @cfg {Number} width (in pixels)
27364 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27367 stylesheets: false,
27372 // private properties
27373 validationEvent : false,
27375 initialized : false,
27378 onFocus : Roo.emptyFn,
27380 hideMode:'offsets',
27382 tbContainer : false,
27386 toolbarContainer :function() {
27387 return this.wrap.select('.x-html-editor-tb',true).first();
27391 * Protected method that will not generally be called directly. It
27392 * is called when the editor creates its toolbar. Override this method if you need to
27393 * add custom toolbar buttons.
27394 * @param {HtmlEditor} editor
27396 createToolbar : function(){
27397 Roo.log('renewing');
27398 Roo.log("create toolbars");
27400 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27401 this.toolbars[0].render(this.toolbarContainer());
27405 // if (!editor.toolbars || !editor.toolbars.length) {
27406 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27409 // for (var i =0 ; i < editor.toolbars.length;i++) {
27410 // editor.toolbars[i] = Roo.factory(
27411 // typeof(editor.toolbars[i]) == 'string' ?
27412 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27413 // Roo.bootstrap.HtmlEditor);
27414 // editor.toolbars[i].init(editor);
27420 onRender : function(ct, position)
27422 // Roo.log("Call onRender: " + this.xtype);
27424 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27426 this.wrap = this.inputEl().wrap({
27427 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27430 this.editorcore.onRender(ct, position);
27432 if (this.resizable) {
27433 this.resizeEl = new Roo.Resizable(this.wrap, {
27437 minHeight : this.height,
27438 height: this.height,
27439 handles : this.resizable,
27442 resize : function(r, w, h) {
27443 _t.onResize(w,h); // -something
27449 this.createToolbar(this);
27452 if(!this.width && this.resizable){
27453 this.setSize(this.wrap.getSize());
27455 if (this.resizeEl) {
27456 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27457 // should trigger onReize..
27463 onResize : function(w, h)
27465 Roo.log('resize: ' +w + ',' + h );
27466 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27470 if(this.inputEl() ){
27471 if(typeof w == 'number'){
27472 var aw = w - this.wrap.getFrameWidth('lr');
27473 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27476 if(typeof h == 'number'){
27477 var tbh = -11; // fixme it needs to tool bar size!
27478 for (var i =0; i < this.toolbars.length;i++) {
27479 // fixme - ask toolbars for heights?
27480 tbh += this.toolbars[i].el.getHeight();
27481 //if (this.toolbars[i].footer) {
27482 // tbh += this.toolbars[i].footer.el.getHeight();
27490 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27491 ah -= 5; // knock a few pixes off for look..
27492 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27496 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27497 this.editorcore.onResize(ew,eh);
27502 * Toggles the editor between standard and source edit mode.
27503 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27505 toggleSourceEdit : function(sourceEditMode)
27507 this.editorcore.toggleSourceEdit(sourceEditMode);
27509 if(this.editorcore.sourceEditMode){
27510 Roo.log('editor - showing textarea');
27513 // Roo.log(this.syncValue());
27515 this.inputEl().removeClass(['hide', 'x-hidden']);
27516 this.inputEl().dom.removeAttribute('tabIndex');
27517 this.inputEl().focus();
27519 Roo.log('editor - hiding textarea');
27521 // Roo.log(this.pushValue());
27524 this.inputEl().addClass(['hide', 'x-hidden']);
27525 this.inputEl().dom.setAttribute('tabIndex', -1);
27526 //this.deferFocus();
27529 if(this.resizable){
27530 this.setSize(this.wrap.getSize());
27533 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27536 // private (for BoxComponent)
27537 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27539 // private (for BoxComponent)
27540 getResizeEl : function(){
27544 // private (for BoxComponent)
27545 getPositionEl : function(){
27550 initEvents : function(){
27551 this.originalValue = this.getValue();
27555 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27558 // markInvalid : Roo.emptyFn,
27560 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27563 // clearInvalid : Roo.emptyFn,
27565 setValue : function(v){
27566 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27567 this.editorcore.pushValue();
27572 deferFocus : function(){
27573 this.focus.defer(10, this);
27577 focus : function(){
27578 this.editorcore.focus();
27584 onDestroy : function(){
27590 for (var i =0; i < this.toolbars.length;i++) {
27591 // fixme - ask toolbars for heights?
27592 this.toolbars[i].onDestroy();
27595 this.wrap.dom.innerHTML = '';
27596 this.wrap.remove();
27601 onFirstFocus : function(){
27602 //Roo.log("onFirstFocus");
27603 this.editorcore.onFirstFocus();
27604 for (var i =0; i < this.toolbars.length;i++) {
27605 this.toolbars[i].onFirstFocus();
27611 syncValue : function()
27613 this.editorcore.syncValue();
27616 pushValue : function()
27618 this.editorcore.pushValue();
27622 // hide stuff that is not compatible
27636 * @event specialkey
27640 * @cfg {String} fieldClass @hide
27643 * @cfg {String} focusClass @hide
27646 * @cfg {String} autoCreate @hide
27649 * @cfg {String} inputType @hide
27653 * @cfg {String} invalidText @hide
27656 * @cfg {String} msgFx @hide
27659 * @cfg {String} validateOnBlur @hide
27668 Roo.namespace('Roo.bootstrap.htmleditor');
27670 * @class Roo.bootstrap.HtmlEditorToolbar1
27676 new Roo.bootstrap.HtmlEditor({
27679 new Roo.bootstrap.HtmlEditorToolbar1({
27680 disable : { fonts: 1 , format: 1, ..., ... , ...],
27686 * @cfg {Object} disable List of elements to disable..
27687 * @cfg {Array} btns List of additional buttons.
27691 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27694 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27697 Roo.apply(this, config);
27699 // default disabled, based on 'good practice'..
27700 this.disable = this.disable || {};
27701 Roo.applyIf(this.disable, {
27704 specialElements : true
27706 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27708 this.editor = config.editor;
27709 this.editorcore = config.editor.editorcore;
27711 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27713 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27714 // dont call parent... till later.
27716 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27721 editorcore : false,
27726 "h1","h2","h3","h4","h5","h6",
27728 "abbr", "acronym", "address", "cite", "samp", "var",
27732 onRender : function(ct, position)
27734 // Roo.log("Call onRender: " + this.xtype);
27736 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27738 this.el.dom.style.marginBottom = '0';
27740 var editorcore = this.editorcore;
27741 var editor= this.editor;
27744 var btn = function(id,cmd , toggle, handler, html){
27746 var event = toggle ? 'toggle' : 'click';
27751 xns: Roo.bootstrap,
27755 enableToggle:toggle !== false,
27757 pressed : toggle ? false : null,
27760 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27761 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27767 // var cb_box = function...
27772 xns: Roo.bootstrap,
27777 xns: Roo.bootstrap,
27781 Roo.each(this.formats, function(f) {
27782 style.menu.items.push({
27784 xns: Roo.bootstrap,
27785 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27790 editorcore.insertTag(this.tagname);
27797 children.push(style);
27799 btn('bold',false,true);
27800 btn('italic',false,true);
27801 btn('align-left', 'justifyleft',true);
27802 btn('align-center', 'justifycenter',true);
27803 btn('align-right' , 'justifyright',true);
27804 btn('link', false, false, function(btn) {
27805 //Roo.log("create link?");
27806 var url = prompt(this.createLinkText, this.defaultLinkValue);
27807 if(url && url != 'http:/'+'/'){
27808 this.editorcore.relayCmd('createlink', url);
27811 btn('list','insertunorderedlist',true);
27812 btn('pencil', false,true, function(btn){
27814 this.toggleSourceEdit(btn.pressed);
27817 if (this.editor.btns.length > 0) {
27818 for (var i = 0; i<this.editor.btns.length; i++) {
27819 children.push(this.editor.btns[i]);
27827 xns: Roo.bootstrap,
27832 xns: Roo.bootstrap,
27837 cog.menu.items.push({
27839 xns: Roo.bootstrap,
27840 html : Clean styles,
27845 editorcore.insertTag(this.tagname);
27854 this.xtype = 'NavSimplebar';
27856 for(var i=0;i< children.length;i++) {
27858 this.buttons.add(this.addxtypeChild(children[i]));
27862 editor.on('editorevent', this.updateToolbar, this);
27864 onBtnClick : function(id)
27866 this.editorcore.relayCmd(id);
27867 this.editorcore.focus();
27871 * Protected method that will not generally be called directly. It triggers
27872 * a toolbar update by reading the markup state of the current selection in the editor.
27874 updateToolbar: function(){
27876 if(!this.editorcore.activated){
27877 this.editor.onFirstFocus(); // is this neeed?
27881 var btns = this.buttons;
27882 var doc = this.editorcore.doc;
27883 btns.get('bold').setActive(doc.queryCommandState('bold'));
27884 btns.get('italic').setActive(doc.queryCommandState('italic'));
27885 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27887 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27888 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27889 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27891 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27892 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27895 var ans = this.editorcore.getAllAncestors();
27896 if (this.formatCombo) {
27899 var store = this.formatCombo.store;
27900 this.formatCombo.setValue("");
27901 for (var i =0; i < ans.length;i++) {
27902 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27904 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27912 // hides menus... - so this cant be on a menu...
27913 Roo.bootstrap.MenuMgr.hideAll();
27915 Roo.bootstrap.MenuMgr.hideAll();
27916 //this.editorsyncValue();
27918 onFirstFocus: function() {
27919 this.buttons.each(function(item){
27923 toggleSourceEdit : function(sourceEditMode){
27926 if(sourceEditMode){
27927 Roo.log("disabling buttons");
27928 this.buttons.each( function(item){
27929 if(item.cmd != 'pencil'){
27935 Roo.log("enabling buttons");
27936 if(this.editorcore.initialized){
27937 this.buttons.each( function(item){
27943 Roo.log("calling toggole on editor");
27944 // tell the editor that it's been pressed..
27945 this.editor.toggleSourceEdit(sourceEditMode);
27959 * @class Roo.bootstrap.Markdown
27960 * @extends Roo.bootstrap.TextArea
27961 * Bootstrap Showdown editable area
27962 * @cfg {string} content
27965 * Create a new Showdown
27968 Roo.bootstrap.Markdown = function(config){
27969 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27973 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27977 initEvents : function()
27980 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27981 this.markdownEl = this.el.createChild({
27982 cls : 'roo-markdown-area'
27984 this.inputEl().addClass('d-none');
27985 if (this.getValue() == '') {
27986 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27989 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27991 this.markdownEl.on('click', this.toggleTextEdit, this);
27992 this.on('blur', this.toggleTextEdit, this);
27993 this.on('specialkey', this.resizeTextArea, this);
27996 toggleTextEdit : function()
27998 var sh = this.markdownEl.getHeight();
27999 this.inputEl().addClass('d-none');
28000 this.markdownEl.addClass('d-none');
28001 if (!this.editing) {
28003 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28004 this.inputEl().removeClass('d-none');
28005 this.inputEl().focus();
28006 this.editing = true;
28009 // show showdown...
28010 this.updateMarkdown();
28011 this.markdownEl.removeClass('d-none');
28012 this.editing = false;
28015 updateMarkdown : function()
28017 if (this.getValue() == '') {
28018 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28022 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28025 resizeTextArea: function () {
28028 Roo.log([sh, this.getValue().split("\n").length * 30]);
28029 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28031 setValue : function(val)
28033 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28034 if (!this.editing) {
28035 this.updateMarkdown();
28041 if (!this.editing) {
28042 this.toggleTextEdit();
28050 * Ext JS Library 1.1.1
28051 * Copyright(c) 2006-2007, Ext JS, LLC.
28053 * Originally Released Under LGPL - original licence link has changed is not relivant.
28056 * <script type="text/javascript">
28060 * @class Roo.bootstrap.PagingToolbar
28061 * @extends Roo.bootstrap.NavSimplebar
28062 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28064 * Create a new PagingToolbar
28065 * @param {Object} config The config object
28066 * @param {Roo.data.Store} store
28068 Roo.bootstrap.PagingToolbar = function(config)
28070 // old args format still supported... - xtype is prefered..
28071 // created from xtype...
28073 this.ds = config.dataSource;
28075 if (config.store && !this.ds) {
28076 this.store= Roo.factory(config.store, Roo.data);
28077 this.ds = this.store;
28078 this.ds.xmodule = this.xmodule || false;
28081 this.toolbarItems = [];
28082 if (config.items) {
28083 this.toolbarItems = config.items;
28086 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28091 this.bind(this.ds);
28094 if (Roo.bootstrap.version == 4) {
28095 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28097 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28102 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28104 * @cfg {Roo.data.Store} dataSource
28105 * The underlying data store providing the paged data
28108 * @cfg {String/HTMLElement/Element} container
28109 * container The id or element that will contain the toolbar
28112 * @cfg {Boolean} displayInfo
28113 * True to display the displayMsg (defaults to false)
28116 * @cfg {Number} pageSize
28117 * The number of records to display per page (defaults to 20)
28121 * @cfg {String} displayMsg
28122 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28124 displayMsg : 'Displaying {0} - {1} of {2}',
28126 * @cfg {String} emptyMsg
28127 * The message to display when no records are found (defaults to "No data to display")
28129 emptyMsg : 'No data to display',
28131 * Customizable piece of the default paging text (defaults to "Page")
28134 beforePageText : "Page",
28136 * Customizable piece of the default paging text (defaults to "of %0")
28139 afterPageText : "of {0}",
28141 * Customizable piece of the default paging text (defaults to "First Page")
28144 firstText : "First Page",
28146 * Customizable piece of the default paging text (defaults to "Previous Page")
28149 prevText : "Previous Page",
28151 * Customizable piece of the default paging text (defaults to "Next Page")
28154 nextText : "Next Page",
28156 * Customizable piece of the default paging text (defaults to "Last Page")
28159 lastText : "Last Page",
28161 * Customizable piece of the default paging text (defaults to "Refresh")
28164 refreshText : "Refresh",
28168 onRender : function(ct, position)
28170 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28171 this.navgroup.parentId = this.id;
28172 this.navgroup.onRender(this.el, null);
28173 // add the buttons to the navgroup
28175 if(this.displayInfo){
28176 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28177 this.displayEl = this.el.select('.x-paging-info', true).first();
28178 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28179 // this.displayEl = navel.el.select('span',true).first();
28185 Roo.each(_this.buttons, function(e){ // this might need to use render????
28186 Roo.factory(e).render(_this.el);
28190 Roo.each(_this.toolbarItems, function(e) {
28191 _this.navgroup.addItem(e);
28195 this.first = this.navgroup.addItem({
28196 tooltip: this.firstText,
28197 cls: "prev btn-outline-secondary",
28198 html : ' <i class="fa fa-step-backward"></i>',
28200 preventDefault: true,
28201 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28204 this.prev = this.navgroup.addItem({
28205 tooltip: this.prevText,
28206 cls: "prev btn-outline-secondary",
28207 html : ' <i class="fa fa-backward"></i>',
28209 preventDefault: true,
28210 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28212 //this.addSeparator();
28215 var field = this.navgroup.addItem( {
28217 cls : 'x-paging-position btn-outline-secondary',
28219 html : this.beforePageText +
28220 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28221 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28224 this.field = field.el.select('input', true).first();
28225 this.field.on("keydown", this.onPagingKeydown, this);
28226 this.field.on("focus", function(){this.dom.select();});
28229 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28230 //this.field.setHeight(18);
28231 //this.addSeparator();
28232 this.next = this.navgroup.addItem({
28233 tooltip: this.nextText,
28234 cls: "next btn-outline-secondary",
28235 html : ' <i class="fa fa-forward"></i>',
28237 preventDefault: true,
28238 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28240 this.last = this.navgroup.addItem({
28241 tooltip: this.lastText,
28242 html : ' <i class="fa fa-step-forward"></i>',
28243 cls: "next btn-outline-secondary",
28245 preventDefault: true,
28246 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28248 //this.addSeparator();
28249 this.loading = this.navgroup.addItem({
28250 tooltip: this.refreshText,
28251 cls: "btn-outline-secondary",
28252 html : ' <i class="fa fa-refresh"></i>',
28253 preventDefault: true,
28254 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28260 updateInfo : function(){
28261 if(this.displayEl){
28262 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28263 var msg = count == 0 ?
28267 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28269 this.displayEl.update(msg);
28274 onLoad : function(ds, r, o)
28276 this.cursor = o.params && o.params.start ? o.params.start : 0;
28278 var d = this.getPageData(),
28283 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28284 this.field.dom.value = ap;
28285 this.first.setDisabled(ap == 1);
28286 this.prev.setDisabled(ap == 1);
28287 this.next.setDisabled(ap == ps);
28288 this.last.setDisabled(ap == ps);
28289 this.loading.enable();
28294 getPageData : function(){
28295 var total = this.ds.getTotalCount();
28298 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28299 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28304 onLoadError : function(){
28305 this.loading.enable();
28309 onPagingKeydown : function(e){
28310 var k = e.getKey();
28311 var d = this.getPageData();
28313 var v = this.field.dom.value, pageNum;
28314 if(!v || isNaN(pageNum = parseInt(v, 10))){
28315 this.field.dom.value = d.activePage;
28318 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28319 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28322 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))
28324 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28325 this.field.dom.value = pageNum;
28326 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28329 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28331 var v = this.field.dom.value, pageNum;
28332 var increment = (e.shiftKey) ? 10 : 1;
28333 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28336 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28337 this.field.dom.value = d.activePage;
28340 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28342 this.field.dom.value = parseInt(v, 10) + increment;
28343 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28344 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28351 beforeLoad : function(){
28353 this.loading.disable();
28358 onClick : function(which){
28367 ds.load({params:{start: 0, limit: this.pageSize}});
28370 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28373 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28376 var total = ds.getTotalCount();
28377 var extra = total % this.pageSize;
28378 var lastStart = extra ? (total - extra) : total-this.pageSize;
28379 ds.load({params:{start: lastStart, limit: this.pageSize}});
28382 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28388 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28389 * @param {Roo.data.Store} store The data store to unbind
28391 unbind : function(ds){
28392 ds.un("beforeload", this.beforeLoad, this);
28393 ds.un("load", this.onLoad, this);
28394 ds.un("loadexception", this.onLoadError, this);
28395 ds.un("remove", this.updateInfo, this);
28396 ds.un("add", this.updateInfo, this);
28397 this.ds = undefined;
28401 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28402 * @param {Roo.data.Store} store The data store to bind
28404 bind : function(ds){
28405 ds.on("beforeload", this.beforeLoad, this);
28406 ds.on("load", this.onLoad, this);
28407 ds.on("loadexception", this.onLoadError, this);
28408 ds.on("remove", this.updateInfo, this);
28409 ds.on("add", this.updateInfo, this);
28420 * @class Roo.bootstrap.MessageBar
28421 * @extends Roo.bootstrap.Component
28422 * Bootstrap MessageBar class
28423 * @cfg {String} html contents of the MessageBar
28424 * @cfg {String} weight (info | success | warning | danger) default info
28425 * @cfg {String} beforeClass insert the bar before the given class
28426 * @cfg {Boolean} closable (true | false) default false
28427 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28430 * Create a new Element
28431 * @param {Object} config The config object
28434 Roo.bootstrap.MessageBar = function(config){
28435 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28438 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28444 beforeClass: 'bootstrap-sticky-wrap',
28446 getAutoCreate : function(){
28450 cls: 'alert alert-dismissable alert-' + this.weight,
28455 html: this.html || ''
28461 cfg.cls += ' alert-messages-fixed';
28475 onRender : function(ct, position)
28477 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28480 var cfg = Roo.apply({}, this.getAutoCreate());
28484 cfg.cls += ' ' + this.cls;
28487 cfg.style = this.style;
28489 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28491 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28494 this.el.select('>button.close').on('click', this.hide, this);
28500 if (!this.rendered) {
28506 this.fireEvent('show', this);
28512 if (!this.rendered) {
28518 this.fireEvent('hide', this);
28521 update : function()
28523 // var e = this.el.dom.firstChild;
28525 // if(this.closable){
28526 // e = e.nextSibling;
28529 // e.data = this.html || '';
28531 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28547 * @class Roo.bootstrap.Graph
28548 * @extends Roo.bootstrap.Component
28549 * Bootstrap Graph class
28553 @cfg {String} graphtype bar | vbar | pie
28554 @cfg {number} g_x coodinator | centre x (pie)
28555 @cfg {number} g_y coodinator | centre y (pie)
28556 @cfg {number} g_r radius (pie)
28557 @cfg {number} g_height height of the chart (respected by all elements in the set)
28558 @cfg {number} g_width width of the chart (respected by all elements in the set)
28559 @cfg {Object} title The title of the chart
28562 -opts (object) options for the chart
28564 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28565 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28567 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.
28568 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28570 o stretch (boolean)
28572 -opts (object) options for the pie
28575 o startAngle (number)
28576 o endAngle (number)
28580 * Create a new Input
28581 * @param {Object} config The config object
28584 Roo.bootstrap.Graph = function(config){
28585 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28591 * The img click event for the img.
28592 * @param {Roo.EventObject} e
28598 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28609 //g_colors: this.colors,
28616 getAutoCreate : function(){
28627 onRender : function(ct,position){
28630 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28632 if (typeof(Raphael) == 'undefined') {
28633 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28637 this.raphael = Raphael(this.el.dom);
28639 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28640 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28641 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28642 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28644 r.text(160, 10, "Single Series Chart").attr(txtattr);
28645 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28646 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28647 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28649 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28650 r.barchart(330, 10, 300, 220, data1);
28651 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28652 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28655 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28656 // r.barchart(30, 30, 560, 250, xdata, {
28657 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28658 // axis : "0 0 1 1",
28659 // axisxlabels : xdata
28660 // //yvalues : cols,
28663 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28665 // this.load(null,xdata,{
28666 // axis : "0 0 1 1",
28667 // axisxlabels : xdata
28672 load : function(graphtype,xdata,opts)
28674 this.raphael.clear();
28676 graphtype = this.graphtype;
28681 var r = this.raphael,
28682 fin = function () {
28683 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28685 fout = function () {
28686 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28688 pfin = function() {
28689 this.sector.stop();
28690 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28693 this.label[0].stop();
28694 this.label[0].attr({ r: 7.5 });
28695 this.label[1].attr({ "font-weight": 800 });
28698 pfout = function() {
28699 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28702 this.label[0].animate({ r: 5 }, 500, "bounce");
28703 this.label[1].attr({ "font-weight": 400 });
28709 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28712 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28715 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28716 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28718 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28725 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28730 setTitle: function(o)
28735 initEvents: function() {
28738 this.el.on('click', this.onClick, this);
28742 onClick : function(e)
28744 Roo.log('img onclick');
28745 this.fireEvent('click', this, e);
28757 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28760 * @class Roo.bootstrap.dash.NumberBox
28761 * @extends Roo.bootstrap.Component
28762 * Bootstrap NumberBox class
28763 * @cfg {String} headline Box headline
28764 * @cfg {String} content Box content
28765 * @cfg {String} icon Box icon
28766 * @cfg {String} footer Footer text
28767 * @cfg {String} fhref Footer href
28770 * Create a new NumberBox
28771 * @param {Object} config The config object
28775 Roo.bootstrap.dash.NumberBox = function(config){
28776 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28780 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28789 getAutoCreate : function(){
28793 cls : 'small-box ',
28801 cls : 'roo-headline',
28802 html : this.headline
28806 cls : 'roo-content',
28807 html : this.content
28821 cls : 'ion ' + this.icon
28830 cls : 'small-box-footer',
28831 href : this.fhref || '#',
28835 cfg.cn.push(footer);
28842 onRender : function(ct,position){
28843 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28850 setHeadline: function (value)
28852 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28855 setFooter: function (value, href)
28857 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28860 this.el.select('a.small-box-footer',true).first().attr('href', href);
28865 setContent: function (value)
28867 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28870 initEvents: function()
28884 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28887 * @class Roo.bootstrap.dash.TabBox
28888 * @extends Roo.bootstrap.Component
28889 * Bootstrap TabBox class
28890 * @cfg {String} title Title of the TabBox
28891 * @cfg {String} icon Icon of the TabBox
28892 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28893 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28896 * Create a new TabBox
28897 * @param {Object} config The config object
28901 Roo.bootstrap.dash.TabBox = function(config){
28902 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28907 * When a pane is added
28908 * @param {Roo.bootstrap.dash.TabPane} pane
28912 * @event activatepane
28913 * When a pane is activated
28914 * @param {Roo.bootstrap.dash.TabPane} pane
28916 "activatepane" : true
28924 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28929 tabScrollable : false,
28931 getChildContainer : function()
28933 return this.el.select('.tab-content', true).first();
28936 getAutoCreate : function(){
28940 cls: 'pull-left header',
28948 cls: 'fa ' + this.icon
28954 cls: 'nav nav-tabs pull-right',
28960 if(this.tabScrollable){
28967 cls: 'nav nav-tabs pull-right',
28978 cls: 'nav-tabs-custom',
28983 cls: 'tab-content no-padding',
28991 initEvents : function()
28993 //Roo.log('add add pane handler');
28994 this.on('addpane', this.onAddPane, this);
28997 * Updates the box title
28998 * @param {String} html to set the title to.
29000 setTitle : function(value)
29002 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29004 onAddPane : function(pane)
29006 this.panes.push(pane);
29007 //Roo.log('addpane');
29009 // tabs are rendere left to right..
29010 if(!this.showtabs){
29014 var ctr = this.el.select('.nav-tabs', true).first();
29017 var existing = ctr.select('.nav-tab',true);
29018 var qty = existing.getCount();;
29021 var tab = ctr.createChild({
29023 cls : 'nav-tab' + (qty ? '' : ' active'),
29031 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29034 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29036 pane.el.addClass('active');
29041 onTabClick : function(ev,un,ob,pane)
29043 //Roo.log('tab - prev default');
29044 ev.preventDefault();
29047 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29048 pane.tab.addClass('active');
29049 //Roo.log(pane.title);
29050 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29051 // technically we should have a deactivate event.. but maybe add later.
29052 // and it should not de-activate the selected tab...
29053 this.fireEvent('activatepane', pane);
29054 pane.el.addClass('active');
29055 pane.fireEvent('activate');
29060 getActivePane : function()
29063 Roo.each(this.panes, function(p) {
29064 if(p.el.hasClass('active')){
29085 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29087 * @class Roo.bootstrap.TabPane
29088 * @extends Roo.bootstrap.Component
29089 * Bootstrap TabPane class
29090 * @cfg {Boolean} active (false | true) Default false
29091 * @cfg {String} title title of panel
29095 * Create a new TabPane
29096 * @param {Object} config The config object
29099 Roo.bootstrap.dash.TabPane = function(config){
29100 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29106 * When a pane is activated
29107 * @param {Roo.bootstrap.dash.TabPane} pane
29114 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29119 // the tabBox that this is attached to.
29122 getAutoCreate : function()
29130 cfg.cls += ' active';
29135 initEvents : function()
29137 //Roo.log('trigger add pane handler');
29138 this.parent().fireEvent('addpane', this)
29142 * Updates the tab title
29143 * @param {String} html to set the title to.
29145 setTitle: function(str)
29151 this.tab.select('a', true).first().dom.innerHTML = str;
29168 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29171 * @class Roo.bootstrap.menu.Menu
29172 * @extends Roo.bootstrap.Component
29173 * Bootstrap Menu class - container for Menu
29174 * @cfg {String} html Text of the menu
29175 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29176 * @cfg {String} icon Font awesome icon
29177 * @cfg {String} pos Menu align to (top | bottom) default bottom
29181 * Create a new Menu
29182 * @param {Object} config The config object
29186 Roo.bootstrap.menu.Menu = function(config){
29187 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29191 * @event beforeshow
29192 * Fires before this menu is displayed
29193 * @param {Roo.bootstrap.menu.Menu} this
29197 * @event beforehide
29198 * Fires before this menu is hidden
29199 * @param {Roo.bootstrap.menu.Menu} this
29204 * Fires after this menu is displayed
29205 * @param {Roo.bootstrap.menu.Menu} this
29210 * Fires after this menu is hidden
29211 * @param {Roo.bootstrap.menu.Menu} this
29216 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29217 * @param {Roo.bootstrap.menu.Menu} this
29218 * @param {Roo.EventObject} e
29225 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29229 weight : 'default',
29234 getChildContainer : function() {
29235 if(this.isSubMenu){
29239 return this.el.select('ul.dropdown-menu', true).first();
29242 getAutoCreate : function()
29247 cls : 'roo-menu-text',
29255 cls : 'fa ' + this.icon
29266 cls : 'dropdown-button btn btn-' + this.weight,
29271 cls : 'dropdown-toggle btn btn-' + this.weight,
29281 cls : 'dropdown-menu'
29287 if(this.pos == 'top'){
29288 cfg.cls += ' dropup';
29291 if(this.isSubMenu){
29294 cls : 'dropdown-menu'
29301 onRender : function(ct, position)
29303 this.isSubMenu = ct.hasClass('dropdown-submenu');
29305 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29308 initEvents : function()
29310 if(this.isSubMenu){
29314 this.hidden = true;
29316 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29317 this.triggerEl.on('click', this.onTriggerPress, this);
29319 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29320 this.buttonEl.on('click', this.onClick, this);
29326 if(this.isSubMenu){
29330 return this.el.select('ul.dropdown-menu', true).first();
29333 onClick : function(e)
29335 this.fireEvent("click", this, e);
29338 onTriggerPress : function(e)
29340 if (this.isVisible()) {
29347 isVisible : function(){
29348 return !this.hidden;
29353 this.fireEvent("beforeshow", this);
29355 this.hidden = false;
29356 this.el.addClass('open');
29358 Roo.get(document).on("mouseup", this.onMouseUp, this);
29360 this.fireEvent("show", this);
29367 this.fireEvent("beforehide", this);
29369 this.hidden = true;
29370 this.el.removeClass('open');
29372 Roo.get(document).un("mouseup", this.onMouseUp);
29374 this.fireEvent("hide", this);
29377 onMouseUp : function()
29391 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29394 * @class Roo.bootstrap.menu.Item
29395 * @extends Roo.bootstrap.Component
29396 * Bootstrap MenuItem class
29397 * @cfg {Boolean} submenu (true | false) default false
29398 * @cfg {String} html text of the item
29399 * @cfg {String} href the link
29400 * @cfg {Boolean} disable (true | false) default false
29401 * @cfg {Boolean} preventDefault (true | false) default true
29402 * @cfg {String} icon Font awesome icon
29403 * @cfg {String} pos Submenu align to (left | right) default right
29407 * Create a new Item
29408 * @param {Object} config The config object
29412 Roo.bootstrap.menu.Item = function(config){
29413 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29417 * Fires when the mouse is hovering over this menu
29418 * @param {Roo.bootstrap.menu.Item} this
29419 * @param {Roo.EventObject} e
29424 * Fires when the mouse exits this menu
29425 * @param {Roo.bootstrap.menu.Item} this
29426 * @param {Roo.EventObject} e
29432 * The raw click event for the entire grid.
29433 * @param {Roo.EventObject} e
29439 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29444 preventDefault: true,
29449 getAutoCreate : function()
29454 cls : 'roo-menu-item-text',
29462 cls : 'fa ' + this.icon
29471 href : this.href || '#',
29478 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29482 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29484 if(this.pos == 'left'){
29485 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29492 initEvents : function()
29494 this.el.on('mouseover', this.onMouseOver, this);
29495 this.el.on('mouseout', this.onMouseOut, this);
29497 this.el.select('a', true).first().on('click', this.onClick, this);
29501 onClick : function(e)
29503 if(this.preventDefault){
29504 e.preventDefault();
29507 this.fireEvent("click", this, e);
29510 onMouseOver : function(e)
29512 if(this.submenu && this.pos == 'left'){
29513 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29516 this.fireEvent("mouseover", this, e);
29519 onMouseOut : function(e)
29521 this.fireEvent("mouseout", this, e);
29533 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29536 * @class Roo.bootstrap.menu.Separator
29537 * @extends Roo.bootstrap.Component
29538 * Bootstrap Separator class
29541 * Create a new Separator
29542 * @param {Object} config The config object
29546 Roo.bootstrap.menu.Separator = function(config){
29547 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29550 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29552 getAutoCreate : function(){
29555 cls: 'dropdown-divider divider'
29573 * @class Roo.bootstrap.Tooltip
29574 * Bootstrap Tooltip class
29575 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29576 * to determine which dom element triggers the tooltip.
29578 * It needs to add support for additional attributes like tooltip-position
29581 * Create a new Toolti
29582 * @param {Object} config The config object
29585 Roo.bootstrap.Tooltip = function(config){
29586 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29588 this.alignment = Roo.bootstrap.Tooltip.alignment;
29590 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29591 this.alignment = config.alignment;
29596 Roo.apply(Roo.bootstrap.Tooltip, {
29598 * @function init initialize tooltip monitoring.
29602 currentTip : false,
29603 currentRegion : false,
29609 Roo.get(document).on('mouseover', this.enter ,this);
29610 Roo.get(document).on('mouseout', this.leave, this);
29613 this.currentTip = new Roo.bootstrap.Tooltip();
29616 enter : function(ev)
29618 var dom = ev.getTarget();
29620 //Roo.log(['enter',dom]);
29621 var el = Roo.fly(dom);
29622 if (this.currentEl) {
29624 //Roo.log(this.currentEl);
29625 //Roo.log(this.currentEl.contains(dom));
29626 if (this.currentEl == el) {
29629 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29635 if (this.currentTip.el) {
29636 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29640 if(!el || el.dom == document){
29646 if (!el.attr('tooltip')) {
29647 pel = el.findParent("[tooltip]");
29649 bindEl = Roo.get(pel);
29655 // you can not look for children, as if el is the body.. then everythign is the child..
29656 if (!pel && !el.attr('tooltip')) { //
29657 if (!el.select("[tooltip]").elements.length) {
29660 // is the mouse over this child...?
29661 bindEl = el.select("[tooltip]").first();
29662 var xy = ev.getXY();
29663 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29664 //Roo.log("not in region.");
29667 //Roo.log("child element over..");
29670 this.currentEl = el;
29671 this.currentTip.bind(bindEl);
29672 this.currentRegion = Roo.lib.Region.getRegion(dom);
29673 this.currentTip.enter();
29676 leave : function(ev)
29678 var dom = ev.getTarget();
29679 //Roo.log(['leave',dom]);
29680 if (!this.currentEl) {
29685 if (dom != this.currentEl.dom) {
29688 var xy = ev.getXY();
29689 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29692 // only activate leave if mouse cursor is outside... bounding box..
29697 if (this.currentTip) {
29698 this.currentTip.leave();
29700 //Roo.log('clear currentEl');
29701 this.currentEl = false;
29706 'left' : ['r-l', [-2,0], 'right'],
29707 'right' : ['l-r', [2,0], 'left'],
29708 'bottom' : ['t-b', [0,2], 'top'],
29709 'top' : [ 'b-t', [0,-2], 'bottom']
29715 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29720 delay : null, // can be { show : 300 , hide: 500}
29724 hoverState : null, //???
29726 placement : 'bottom',
29730 getAutoCreate : function(){
29737 cls : 'tooltip-arrow arrow'
29740 cls : 'tooltip-inner'
29747 bind : function(el)
29752 initEvents : function()
29754 this.arrowEl = this.el.select('.arrow', true).first();
29755 this.innerEl = this.el.select('.tooltip-inner', true).first();
29758 enter : function () {
29760 if (this.timeout != null) {
29761 clearTimeout(this.timeout);
29764 this.hoverState = 'in';
29765 //Roo.log("enter - show");
29766 if (!this.delay || !this.delay.show) {
29771 this.timeout = setTimeout(function () {
29772 if (_t.hoverState == 'in') {
29775 }, this.delay.show);
29779 clearTimeout(this.timeout);
29781 this.hoverState = 'out';
29782 if (!this.delay || !this.delay.hide) {
29788 this.timeout = setTimeout(function () {
29789 //Roo.log("leave - timeout");
29791 if (_t.hoverState == 'out') {
29793 Roo.bootstrap.Tooltip.currentEl = false;
29798 show : function (msg)
29801 this.render(document.body);
29804 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29806 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29808 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29810 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29811 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29813 var placement = typeof this.placement == 'function' ?
29814 this.placement.call(this, this.el, on_el) :
29817 var autoToken = /\s?auto?\s?/i;
29818 var autoPlace = autoToken.test(placement);
29820 placement = placement.replace(autoToken, '') || 'top';
29824 //this.el.setXY([0,0]);
29826 //this.el.dom.style.display='block';
29828 //this.el.appendTo(on_el);
29830 var p = this.getPosition();
29831 var box = this.el.getBox();
29837 var align = this.alignment[placement];
29839 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29841 if(placement == 'top' || placement == 'bottom'){
29843 placement = 'right';
29846 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29847 placement = 'left';
29850 var scroll = Roo.select('body', true).first().getScroll();
29852 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29856 align = this.alignment[placement];
29858 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29862 var elems = document.getElementsByTagName('div');
29863 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29864 for (var i = 0; i < elems.length; i++) {
29865 var zindex = Number.parseInt(
29866 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29869 if (zindex > highest) {
29876 this.el.dom.style.zIndex = highest;
29878 this.el.alignTo(this.bindEl, align[0],align[1]);
29879 //var arrow = this.el.select('.arrow',true).first();
29880 //arrow.set(align[2],
29882 this.el.addClass(placement);
29883 this.el.addClass("bs-tooltip-"+ placement);
29885 this.el.addClass('in fade show');
29887 this.hoverState = null;
29889 if (this.el.hasClass('fade')) {
29904 //this.el.setXY([0,0]);
29905 this.el.removeClass(['show', 'in']);
29921 * @class Roo.bootstrap.LocationPicker
29922 * @extends Roo.bootstrap.Component
29923 * Bootstrap LocationPicker class
29924 * @cfg {Number} latitude Position when init default 0
29925 * @cfg {Number} longitude Position when init default 0
29926 * @cfg {Number} zoom default 15
29927 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29928 * @cfg {Boolean} mapTypeControl default false
29929 * @cfg {Boolean} disableDoubleClickZoom default false
29930 * @cfg {Boolean} scrollwheel default true
29931 * @cfg {Boolean} streetViewControl default false
29932 * @cfg {Number} radius default 0
29933 * @cfg {String} locationName
29934 * @cfg {Boolean} draggable default true
29935 * @cfg {Boolean} enableAutocomplete default false
29936 * @cfg {Boolean} enableReverseGeocode default true
29937 * @cfg {String} markerTitle
29940 * Create a new LocationPicker
29941 * @param {Object} config The config object
29945 Roo.bootstrap.LocationPicker = function(config){
29947 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29952 * Fires when the picker initialized.
29953 * @param {Roo.bootstrap.LocationPicker} this
29954 * @param {Google Location} location
29958 * @event positionchanged
29959 * Fires when the picker position changed.
29960 * @param {Roo.bootstrap.LocationPicker} this
29961 * @param {Google Location} location
29963 positionchanged : true,
29966 * Fires when the map resize.
29967 * @param {Roo.bootstrap.LocationPicker} this
29972 * Fires when the map show.
29973 * @param {Roo.bootstrap.LocationPicker} this
29978 * Fires when the map hide.
29979 * @param {Roo.bootstrap.LocationPicker} this
29984 * Fires when click the map.
29985 * @param {Roo.bootstrap.LocationPicker} this
29986 * @param {Map event} e
29990 * @event mapRightClick
29991 * Fires when right click the map.
29992 * @param {Roo.bootstrap.LocationPicker} this
29993 * @param {Map event} e
29995 mapRightClick : true,
29997 * @event markerClick
29998 * Fires when click the marker.
29999 * @param {Roo.bootstrap.LocationPicker} this
30000 * @param {Map event} e
30002 markerClick : true,
30004 * @event markerRightClick
30005 * Fires when right click the marker.
30006 * @param {Roo.bootstrap.LocationPicker} this
30007 * @param {Map event} e
30009 markerRightClick : true,
30011 * @event OverlayViewDraw
30012 * Fires when OverlayView Draw
30013 * @param {Roo.bootstrap.LocationPicker} this
30015 OverlayViewDraw : true,
30017 * @event OverlayViewOnAdd
30018 * Fires when OverlayView Draw
30019 * @param {Roo.bootstrap.LocationPicker} this
30021 OverlayViewOnAdd : true,
30023 * @event OverlayViewOnRemove
30024 * Fires when OverlayView Draw
30025 * @param {Roo.bootstrap.LocationPicker} this
30027 OverlayViewOnRemove : true,
30029 * @event OverlayViewShow
30030 * Fires when OverlayView Draw
30031 * @param {Roo.bootstrap.LocationPicker} this
30032 * @param {Pixel} cpx
30034 OverlayViewShow : true,
30036 * @event OverlayViewHide
30037 * Fires when OverlayView Draw
30038 * @param {Roo.bootstrap.LocationPicker} this
30040 OverlayViewHide : true,
30042 * @event loadexception
30043 * Fires when load google lib failed.
30044 * @param {Roo.bootstrap.LocationPicker} this
30046 loadexception : true
30051 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30053 gMapContext: false,
30059 mapTypeControl: false,
30060 disableDoubleClickZoom: false,
30062 streetViewControl: false,
30066 enableAutocomplete: false,
30067 enableReverseGeocode: true,
30070 getAutoCreate: function()
30075 cls: 'roo-location-picker'
30081 initEvents: function(ct, position)
30083 if(!this.el.getWidth() || this.isApplied()){
30087 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30092 initial: function()
30094 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30095 this.fireEvent('loadexception', this);
30099 if(!this.mapTypeId){
30100 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30103 this.gMapContext = this.GMapContext();
30105 this.initOverlayView();
30107 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30111 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30112 _this.setPosition(_this.gMapContext.marker.position);
30115 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30116 _this.fireEvent('mapClick', this, event);
30120 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30121 _this.fireEvent('mapRightClick', this, event);
30125 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30126 _this.fireEvent('markerClick', this, event);
30130 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30131 _this.fireEvent('markerRightClick', this, event);
30135 this.setPosition(this.gMapContext.location);
30137 this.fireEvent('initial', this, this.gMapContext.location);
30140 initOverlayView: function()
30144 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30148 _this.fireEvent('OverlayViewDraw', _this);
30153 _this.fireEvent('OverlayViewOnAdd', _this);
30156 onRemove: function()
30158 _this.fireEvent('OverlayViewOnRemove', _this);
30161 show: function(cpx)
30163 _this.fireEvent('OverlayViewShow', _this, cpx);
30168 _this.fireEvent('OverlayViewHide', _this);
30174 fromLatLngToContainerPixel: function(event)
30176 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30179 isApplied: function()
30181 return this.getGmapContext() == false ? false : true;
30184 getGmapContext: function()
30186 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30189 GMapContext: function()
30191 var position = new google.maps.LatLng(this.latitude, this.longitude);
30193 var _map = new google.maps.Map(this.el.dom, {
30196 mapTypeId: this.mapTypeId,
30197 mapTypeControl: this.mapTypeControl,
30198 disableDoubleClickZoom: this.disableDoubleClickZoom,
30199 scrollwheel: this.scrollwheel,
30200 streetViewControl: this.streetViewControl,
30201 locationName: this.locationName,
30202 draggable: this.draggable,
30203 enableAutocomplete: this.enableAutocomplete,
30204 enableReverseGeocode: this.enableReverseGeocode
30207 var _marker = new google.maps.Marker({
30208 position: position,
30210 title: this.markerTitle,
30211 draggable: this.draggable
30218 location: position,
30219 radius: this.radius,
30220 locationName: this.locationName,
30221 addressComponents: {
30222 formatted_address: null,
30223 addressLine1: null,
30224 addressLine2: null,
30226 streetNumber: null,
30230 stateOrProvince: null
30233 domContainer: this.el.dom,
30234 geodecoder: new google.maps.Geocoder()
30238 drawCircle: function(center, radius, options)
30240 if (this.gMapContext.circle != null) {
30241 this.gMapContext.circle.setMap(null);
30245 options = Roo.apply({}, options, {
30246 strokeColor: "#0000FF",
30247 strokeOpacity: .35,
30249 fillColor: "#0000FF",
30253 options.map = this.gMapContext.map;
30254 options.radius = radius;
30255 options.center = center;
30256 this.gMapContext.circle = new google.maps.Circle(options);
30257 return this.gMapContext.circle;
30263 setPosition: function(location)
30265 this.gMapContext.location = location;
30266 this.gMapContext.marker.setPosition(location);
30267 this.gMapContext.map.panTo(location);
30268 this.drawCircle(location, this.gMapContext.radius, {});
30272 if (this.gMapContext.settings.enableReverseGeocode) {
30273 this.gMapContext.geodecoder.geocode({
30274 latLng: this.gMapContext.location
30275 }, function(results, status) {
30277 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30278 _this.gMapContext.locationName = results[0].formatted_address;
30279 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30281 _this.fireEvent('positionchanged', this, location);
30288 this.fireEvent('positionchanged', this, location);
30293 google.maps.event.trigger(this.gMapContext.map, "resize");
30295 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30297 this.fireEvent('resize', this);
30300 setPositionByLatLng: function(latitude, longitude)
30302 this.setPosition(new google.maps.LatLng(latitude, longitude));
30305 getCurrentPosition: function()
30308 latitude: this.gMapContext.location.lat(),
30309 longitude: this.gMapContext.location.lng()
30313 getAddressName: function()
30315 return this.gMapContext.locationName;
30318 getAddressComponents: function()
30320 return this.gMapContext.addressComponents;
30323 address_component_from_google_geocode: function(address_components)
30327 for (var i = 0; i < address_components.length; i++) {
30328 var component = address_components[i];
30329 if (component.types.indexOf("postal_code") >= 0) {
30330 result.postalCode = component.short_name;
30331 } else if (component.types.indexOf("street_number") >= 0) {
30332 result.streetNumber = component.short_name;
30333 } else if (component.types.indexOf("route") >= 0) {
30334 result.streetName = component.short_name;
30335 } else if (component.types.indexOf("neighborhood") >= 0) {
30336 result.city = component.short_name;
30337 } else if (component.types.indexOf("locality") >= 0) {
30338 result.city = component.short_name;
30339 } else if (component.types.indexOf("sublocality") >= 0) {
30340 result.district = component.short_name;
30341 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30342 result.stateOrProvince = component.short_name;
30343 } else if (component.types.indexOf("country") >= 0) {
30344 result.country = component.short_name;
30348 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30349 result.addressLine2 = "";
30353 setZoomLevel: function(zoom)
30355 this.gMapContext.map.setZoom(zoom);
30368 this.fireEvent('show', this);
30379 this.fireEvent('hide', this);
30384 Roo.apply(Roo.bootstrap.LocationPicker, {
30386 OverlayView : function(map, options)
30388 options = options || {};
30395 * @class Roo.bootstrap.Alert
30396 * @extends Roo.bootstrap.Component
30397 * Bootstrap Alert class - shows an alert area box
30399 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30400 Enter a valid email address
30403 * @cfg {String} title The title of alert
30404 * @cfg {String} html The content of alert
30405 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30406 * @cfg {String} fa font-awesomeicon
30407 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30408 * @cfg {Boolean} close true to show a x closer
30412 * Create a new alert
30413 * @param {Object} config The config object
30417 Roo.bootstrap.Alert = function(config){
30418 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30422 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30428 faicon: false, // BC
30432 getAutoCreate : function()
30444 style : this.close ? '' : 'display:none'
30448 cls : 'roo-alert-icon'
30453 cls : 'roo-alert-title',
30458 cls : 'roo-alert-text',
30465 cfg.cn[0].cls += ' fa ' + this.faicon;
30468 cfg.cn[0].cls += ' fa ' + this.fa;
30472 cfg.cls += ' alert-' + this.weight;
30478 initEvents: function()
30480 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30481 this.titleEl = this.el.select('.roo-alert-title',true).first();
30482 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30483 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30484 if (this.seconds > 0) {
30485 this.hide.defer(this.seconds, this);
30489 * Set the Title Message HTML
30490 * @param {String} html
30492 setTitle : function(str)
30494 this.titleEl.dom.innerHTML = str;
30498 * Set the Body Message HTML
30499 * @param {String} html
30501 setHtml : function(str)
30503 this.htmlEl.dom.innerHTML = str;
30506 * Set the Weight of the alert
30507 * @param {String} (success|info|warning|danger) weight
30510 setWeight : function(weight)
30513 this.el.removeClass('alert-' + this.weight);
30516 this.weight = weight;
30518 this.el.addClass('alert-' + this.weight);
30521 * Set the Icon of the alert
30522 * @param {String} see fontawsome names (name without the 'fa-' bit)
30524 setIcon : function(icon)
30527 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30530 this.faicon = icon;
30532 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30557 * @class Roo.bootstrap.UploadCropbox
30558 * @extends Roo.bootstrap.Component
30559 * Bootstrap UploadCropbox class
30560 * @cfg {String} emptyText show when image has been loaded
30561 * @cfg {String} rotateNotify show when image too small to rotate
30562 * @cfg {Number} errorTimeout default 3000
30563 * @cfg {Number} minWidth default 300
30564 * @cfg {Number} minHeight default 300
30565 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30566 * @cfg {Boolean} isDocument (true|false) default false
30567 * @cfg {String} url action url
30568 * @cfg {String} paramName default 'imageUpload'
30569 * @cfg {String} method default POST
30570 * @cfg {Boolean} loadMask (true|false) default true
30571 * @cfg {Boolean} loadingText default 'Loading...'
30574 * Create a new UploadCropbox
30575 * @param {Object} config The config object
30578 Roo.bootstrap.UploadCropbox = function(config){
30579 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30583 * @event beforeselectfile
30584 * Fire before select file
30585 * @param {Roo.bootstrap.UploadCropbox} this
30587 "beforeselectfile" : true,
30590 * Fire after initEvent
30591 * @param {Roo.bootstrap.UploadCropbox} this
30596 * Fire after initEvent
30597 * @param {Roo.bootstrap.UploadCropbox} this
30598 * @param {String} data
30603 * Fire when preparing the file data
30604 * @param {Roo.bootstrap.UploadCropbox} this
30605 * @param {Object} file
30610 * Fire when get exception
30611 * @param {Roo.bootstrap.UploadCropbox} this
30612 * @param {XMLHttpRequest} xhr
30614 "exception" : true,
30616 * @event beforeloadcanvas
30617 * Fire before load the canvas
30618 * @param {Roo.bootstrap.UploadCropbox} this
30619 * @param {String} src
30621 "beforeloadcanvas" : true,
30624 * Fire when trash image
30625 * @param {Roo.bootstrap.UploadCropbox} this
30630 * Fire when download the image
30631 * @param {Roo.bootstrap.UploadCropbox} this
30635 * @event footerbuttonclick
30636 * Fire when footerbuttonclick
30637 * @param {Roo.bootstrap.UploadCropbox} this
30638 * @param {String} type
30640 "footerbuttonclick" : true,
30644 * @param {Roo.bootstrap.UploadCropbox} this
30649 * Fire when rotate the image
30650 * @param {Roo.bootstrap.UploadCropbox} this
30651 * @param {String} pos
30656 * Fire when inspect the file
30657 * @param {Roo.bootstrap.UploadCropbox} this
30658 * @param {Object} file
30663 * Fire when xhr upload the file
30664 * @param {Roo.bootstrap.UploadCropbox} this
30665 * @param {Object} data
30670 * Fire when arrange the file data
30671 * @param {Roo.bootstrap.UploadCropbox} this
30672 * @param {Object} formData
30677 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30680 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30682 emptyText : 'Click to upload image',
30683 rotateNotify : 'Image is too small to rotate',
30684 errorTimeout : 3000,
30698 cropType : 'image/jpeg',
30700 canvasLoaded : false,
30701 isDocument : false,
30703 paramName : 'imageUpload',
30705 loadingText : 'Loading...',
30708 getAutoCreate : function()
30712 cls : 'roo-upload-cropbox',
30716 cls : 'roo-upload-cropbox-selector',
30721 cls : 'roo-upload-cropbox-body',
30722 style : 'cursor:pointer',
30726 cls : 'roo-upload-cropbox-preview'
30730 cls : 'roo-upload-cropbox-thumb'
30734 cls : 'roo-upload-cropbox-empty-notify',
30735 html : this.emptyText
30739 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30740 html : this.rotateNotify
30746 cls : 'roo-upload-cropbox-footer',
30749 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30759 onRender : function(ct, position)
30761 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30763 if (this.buttons.length) {
30765 Roo.each(this.buttons, function(bb) {
30767 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30769 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30775 this.maskEl = this.el;
30779 initEvents : function()
30781 this.urlAPI = (window.createObjectURL && window) ||
30782 (window.URL && URL.revokeObjectURL && URL) ||
30783 (window.webkitURL && webkitURL);
30785 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30786 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30788 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30789 this.selectorEl.hide();
30791 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30792 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30794 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30795 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30796 this.thumbEl.hide();
30798 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30799 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30801 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30802 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30803 this.errorEl.hide();
30805 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30806 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30807 this.footerEl.hide();
30809 this.setThumbBoxSize();
30815 this.fireEvent('initial', this);
30822 window.addEventListener("resize", function() { _this.resize(); } );
30824 this.bodyEl.on('click', this.beforeSelectFile, this);
30827 this.bodyEl.on('touchstart', this.onTouchStart, this);
30828 this.bodyEl.on('touchmove', this.onTouchMove, this);
30829 this.bodyEl.on('touchend', this.onTouchEnd, this);
30833 this.bodyEl.on('mousedown', this.onMouseDown, this);
30834 this.bodyEl.on('mousemove', this.onMouseMove, this);
30835 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30836 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30837 Roo.get(document).on('mouseup', this.onMouseUp, this);
30840 this.selectorEl.on('change', this.onFileSelected, this);
30846 this.baseScale = 1;
30848 this.baseRotate = 1;
30849 this.dragable = false;
30850 this.pinching = false;
30853 this.cropData = false;
30854 this.notifyEl.dom.innerHTML = this.emptyText;
30856 this.selectorEl.dom.value = '';
30860 resize : function()
30862 if(this.fireEvent('resize', this) != false){
30863 this.setThumbBoxPosition();
30864 this.setCanvasPosition();
30868 onFooterButtonClick : function(e, el, o, type)
30871 case 'rotate-left' :
30872 this.onRotateLeft(e);
30874 case 'rotate-right' :
30875 this.onRotateRight(e);
30878 this.beforeSelectFile(e);
30893 this.fireEvent('footerbuttonclick', this, type);
30896 beforeSelectFile : function(e)
30898 e.preventDefault();
30900 if(this.fireEvent('beforeselectfile', this) != false){
30901 this.selectorEl.dom.click();
30905 onFileSelected : function(e)
30907 e.preventDefault();
30909 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30913 var file = this.selectorEl.dom.files[0];
30915 if(this.fireEvent('inspect', this, file) != false){
30916 this.prepare(file);
30921 trash : function(e)
30923 this.fireEvent('trash', this);
30926 download : function(e)
30928 this.fireEvent('download', this);
30931 loadCanvas : function(src)
30933 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30937 this.imageEl = document.createElement('img');
30941 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30943 this.imageEl.src = src;
30947 onLoadCanvas : function()
30949 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30950 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30952 this.bodyEl.un('click', this.beforeSelectFile, this);
30954 this.notifyEl.hide();
30955 this.thumbEl.show();
30956 this.footerEl.show();
30958 this.baseRotateLevel();
30960 if(this.isDocument){
30961 this.setThumbBoxSize();
30964 this.setThumbBoxPosition();
30966 this.baseScaleLevel();
30972 this.canvasLoaded = true;
30975 this.maskEl.unmask();
30980 setCanvasPosition : function()
30982 if(!this.canvasEl){
30986 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30987 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30989 this.previewEl.setLeft(pw);
30990 this.previewEl.setTop(ph);
30994 onMouseDown : function(e)
30998 this.dragable = true;
30999 this.pinching = false;
31001 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31002 this.dragable = false;
31006 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31007 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31011 onMouseMove : function(e)
31015 if(!this.canvasLoaded){
31019 if (!this.dragable){
31023 var minX = Math.ceil(this.thumbEl.getLeft(true));
31024 var minY = Math.ceil(this.thumbEl.getTop(true));
31026 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31027 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31029 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31030 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31032 x = x - this.mouseX;
31033 y = y - this.mouseY;
31035 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31036 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31038 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31039 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31041 this.previewEl.setLeft(bgX);
31042 this.previewEl.setTop(bgY);
31044 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31045 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31048 onMouseUp : function(e)
31052 this.dragable = false;
31055 onMouseWheel : function(e)
31059 this.startScale = this.scale;
31061 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31063 if(!this.zoomable()){
31064 this.scale = this.startScale;
31073 zoomable : function()
31075 var minScale = this.thumbEl.getWidth() / this.minWidth;
31077 if(this.minWidth < this.minHeight){
31078 minScale = this.thumbEl.getHeight() / this.minHeight;
31081 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31082 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31086 (this.rotate == 0 || this.rotate == 180) &&
31088 width > this.imageEl.OriginWidth ||
31089 height > this.imageEl.OriginHeight ||
31090 (width < this.minWidth && height < this.minHeight)
31098 (this.rotate == 90 || this.rotate == 270) &&
31100 width > this.imageEl.OriginWidth ||
31101 height > this.imageEl.OriginHeight ||
31102 (width < this.minHeight && height < this.minWidth)
31109 !this.isDocument &&
31110 (this.rotate == 0 || this.rotate == 180) &&
31112 width < this.minWidth ||
31113 width > this.imageEl.OriginWidth ||
31114 height < this.minHeight ||
31115 height > this.imageEl.OriginHeight
31122 !this.isDocument &&
31123 (this.rotate == 90 || this.rotate == 270) &&
31125 width < this.minHeight ||
31126 width > this.imageEl.OriginWidth ||
31127 height < this.minWidth ||
31128 height > this.imageEl.OriginHeight
31138 onRotateLeft : function(e)
31140 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31142 var minScale = this.thumbEl.getWidth() / this.minWidth;
31144 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31145 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31147 this.startScale = this.scale;
31149 while (this.getScaleLevel() < minScale){
31151 this.scale = this.scale + 1;
31153 if(!this.zoomable()){
31158 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31159 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31164 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31171 this.scale = this.startScale;
31173 this.onRotateFail();
31178 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31180 if(this.isDocument){
31181 this.setThumbBoxSize();
31182 this.setThumbBoxPosition();
31183 this.setCanvasPosition();
31188 this.fireEvent('rotate', this, 'left');
31192 onRotateRight : function(e)
31194 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31196 var minScale = this.thumbEl.getWidth() / this.minWidth;
31198 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31199 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31201 this.startScale = this.scale;
31203 while (this.getScaleLevel() < minScale){
31205 this.scale = this.scale + 1;
31207 if(!this.zoomable()){
31212 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31213 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31218 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31225 this.scale = this.startScale;
31227 this.onRotateFail();
31232 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31234 if(this.isDocument){
31235 this.setThumbBoxSize();
31236 this.setThumbBoxPosition();
31237 this.setCanvasPosition();
31242 this.fireEvent('rotate', this, 'right');
31245 onRotateFail : function()
31247 this.errorEl.show(true);
31251 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31256 this.previewEl.dom.innerHTML = '';
31258 var canvasEl = document.createElement("canvas");
31260 var contextEl = canvasEl.getContext("2d");
31262 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31263 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31264 var center = this.imageEl.OriginWidth / 2;
31266 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31267 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31268 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31269 center = this.imageEl.OriginHeight / 2;
31272 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31274 contextEl.translate(center, center);
31275 contextEl.rotate(this.rotate * Math.PI / 180);
31277 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31279 this.canvasEl = document.createElement("canvas");
31281 this.contextEl = this.canvasEl.getContext("2d");
31283 switch (this.rotate) {
31286 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31287 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31289 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31294 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31295 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31297 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31298 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);
31302 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31307 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31308 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31310 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31311 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);
31315 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31320 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31321 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31323 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31324 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31328 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31335 this.previewEl.appendChild(this.canvasEl);
31337 this.setCanvasPosition();
31342 if(!this.canvasLoaded){
31346 var imageCanvas = document.createElement("canvas");
31348 var imageContext = imageCanvas.getContext("2d");
31350 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31351 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31353 var center = imageCanvas.width / 2;
31355 imageContext.translate(center, center);
31357 imageContext.rotate(this.rotate * Math.PI / 180);
31359 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31361 var canvas = document.createElement("canvas");
31363 var context = canvas.getContext("2d");
31365 canvas.width = this.minWidth;
31366 canvas.height = this.minHeight;
31368 switch (this.rotate) {
31371 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31372 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31374 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31375 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31377 var targetWidth = this.minWidth - 2 * x;
31378 var targetHeight = this.minHeight - 2 * y;
31382 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31383 scale = targetWidth / width;
31386 if(x > 0 && y == 0){
31387 scale = targetHeight / height;
31390 if(x > 0 && y > 0){
31391 scale = targetWidth / width;
31393 if(width < height){
31394 scale = targetHeight / height;
31398 context.scale(scale, scale);
31400 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31401 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31403 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31404 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31406 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31411 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31412 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31414 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31415 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31417 var targetWidth = this.minWidth - 2 * x;
31418 var targetHeight = this.minHeight - 2 * y;
31422 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31423 scale = targetWidth / width;
31426 if(x > 0 && y == 0){
31427 scale = targetHeight / height;
31430 if(x > 0 && y > 0){
31431 scale = targetWidth / width;
31433 if(width < height){
31434 scale = targetHeight / height;
31438 context.scale(scale, scale);
31440 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31441 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31443 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31444 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31446 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31448 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31453 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31454 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31456 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31457 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31459 var targetWidth = this.minWidth - 2 * x;
31460 var targetHeight = this.minHeight - 2 * y;
31464 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31465 scale = targetWidth / width;
31468 if(x > 0 && y == 0){
31469 scale = targetHeight / height;
31472 if(x > 0 && y > 0){
31473 scale = targetWidth / width;
31475 if(width < height){
31476 scale = targetHeight / height;
31480 context.scale(scale, scale);
31482 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31483 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31485 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31486 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31488 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31489 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31491 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31496 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31497 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31499 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31500 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31502 var targetWidth = this.minWidth - 2 * x;
31503 var targetHeight = this.minHeight - 2 * y;
31507 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31508 scale = targetWidth / width;
31511 if(x > 0 && y == 0){
31512 scale = targetHeight / height;
31515 if(x > 0 && y > 0){
31516 scale = targetWidth / width;
31518 if(width < height){
31519 scale = targetHeight / height;
31523 context.scale(scale, scale);
31525 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31526 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31528 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31529 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31531 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31533 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31540 this.cropData = canvas.toDataURL(this.cropType);
31542 if(this.fireEvent('crop', this, this.cropData) !== false){
31543 this.process(this.file, this.cropData);
31550 setThumbBoxSize : function()
31554 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31555 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31556 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31558 this.minWidth = width;
31559 this.minHeight = height;
31561 if(this.rotate == 90 || this.rotate == 270){
31562 this.minWidth = height;
31563 this.minHeight = width;
31568 width = Math.ceil(this.minWidth * height / this.minHeight);
31570 if(this.minWidth > this.minHeight){
31572 height = Math.ceil(this.minHeight * width / this.minWidth);
31575 this.thumbEl.setStyle({
31576 width : width + 'px',
31577 height : height + 'px'
31584 setThumbBoxPosition : function()
31586 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31587 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31589 this.thumbEl.setLeft(x);
31590 this.thumbEl.setTop(y);
31594 baseRotateLevel : function()
31596 this.baseRotate = 1;
31599 typeof(this.exif) != 'undefined' &&
31600 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31601 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31603 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31606 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31610 baseScaleLevel : function()
31614 if(this.isDocument){
31616 if(this.baseRotate == 6 || this.baseRotate == 8){
31618 height = this.thumbEl.getHeight();
31619 this.baseScale = height / this.imageEl.OriginWidth;
31621 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31622 width = this.thumbEl.getWidth();
31623 this.baseScale = width / this.imageEl.OriginHeight;
31629 height = this.thumbEl.getHeight();
31630 this.baseScale = height / this.imageEl.OriginHeight;
31632 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31633 width = this.thumbEl.getWidth();
31634 this.baseScale = width / this.imageEl.OriginWidth;
31640 if(this.baseRotate == 6 || this.baseRotate == 8){
31642 width = this.thumbEl.getHeight();
31643 this.baseScale = width / this.imageEl.OriginHeight;
31645 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31646 height = this.thumbEl.getWidth();
31647 this.baseScale = height / this.imageEl.OriginHeight;
31650 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31651 height = this.thumbEl.getWidth();
31652 this.baseScale = height / this.imageEl.OriginHeight;
31654 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31655 width = this.thumbEl.getHeight();
31656 this.baseScale = width / this.imageEl.OriginWidth;
31663 width = this.thumbEl.getWidth();
31664 this.baseScale = width / this.imageEl.OriginWidth;
31666 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31667 height = this.thumbEl.getHeight();
31668 this.baseScale = height / this.imageEl.OriginHeight;
31671 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31673 height = this.thumbEl.getHeight();
31674 this.baseScale = height / this.imageEl.OriginHeight;
31676 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31677 width = this.thumbEl.getWidth();
31678 this.baseScale = width / this.imageEl.OriginWidth;
31686 getScaleLevel : function()
31688 return this.baseScale * Math.pow(1.1, this.scale);
31691 onTouchStart : function(e)
31693 if(!this.canvasLoaded){
31694 this.beforeSelectFile(e);
31698 var touches = e.browserEvent.touches;
31704 if(touches.length == 1){
31705 this.onMouseDown(e);
31709 if(touches.length != 2){
31715 for(var i = 0, finger; finger = touches[i]; i++){
31716 coords.push(finger.pageX, finger.pageY);
31719 var x = Math.pow(coords[0] - coords[2], 2);
31720 var y = Math.pow(coords[1] - coords[3], 2);
31722 this.startDistance = Math.sqrt(x + y);
31724 this.startScale = this.scale;
31726 this.pinching = true;
31727 this.dragable = false;
31731 onTouchMove : function(e)
31733 if(!this.pinching && !this.dragable){
31737 var touches = e.browserEvent.touches;
31744 this.onMouseMove(e);
31750 for(var i = 0, finger; finger = touches[i]; i++){
31751 coords.push(finger.pageX, finger.pageY);
31754 var x = Math.pow(coords[0] - coords[2], 2);
31755 var y = Math.pow(coords[1] - coords[3], 2);
31757 this.endDistance = Math.sqrt(x + y);
31759 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31761 if(!this.zoomable()){
31762 this.scale = this.startScale;
31770 onTouchEnd : function(e)
31772 this.pinching = false;
31773 this.dragable = false;
31777 process : function(file, crop)
31780 this.maskEl.mask(this.loadingText);
31783 this.xhr = new XMLHttpRequest();
31785 file.xhr = this.xhr;
31787 this.xhr.open(this.method, this.url, true);
31790 "Accept": "application/json",
31791 "Cache-Control": "no-cache",
31792 "X-Requested-With": "XMLHttpRequest"
31795 for (var headerName in headers) {
31796 var headerValue = headers[headerName];
31798 this.xhr.setRequestHeader(headerName, headerValue);
31804 this.xhr.onload = function()
31806 _this.xhrOnLoad(_this.xhr);
31809 this.xhr.onerror = function()
31811 _this.xhrOnError(_this.xhr);
31814 var formData = new FormData();
31816 formData.append('returnHTML', 'NO');
31819 formData.append('crop', crop);
31822 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31823 formData.append(this.paramName, file, file.name);
31826 if(typeof(file.filename) != 'undefined'){
31827 formData.append('filename', file.filename);
31830 if(typeof(file.mimetype) != 'undefined'){
31831 formData.append('mimetype', file.mimetype);
31834 if(this.fireEvent('arrange', this, formData) != false){
31835 this.xhr.send(formData);
31839 xhrOnLoad : function(xhr)
31842 this.maskEl.unmask();
31845 if (xhr.readyState !== 4) {
31846 this.fireEvent('exception', this, xhr);
31850 var response = Roo.decode(xhr.responseText);
31852 if(!response.success){
31853 this.fireEvent('exception', this, xhr);
31857 var response = Roo.decode(xhr.responseText);
31859 this.fireEvent('upload', this, response);
31863 xhrOnError : function()
31866 this.maskEl.unmask();
31869 Roo.log('xhr on error');
31871 var response = Roo.decode(xhr.responseText);
31877 prepare : function(file)
31880 this.maskEl.mask(this.loadingText);
31886 if(typeof(file) === 'string'){
31887 this.loadCanvas(file);
31891 if(!file || !this.urlAPI){
31896 this.cropType = file.type;
31900 if(this.fireEvent('prepare', this, this.file) != false){
31902 var reader = new FileReader();
31904 reader.onload = function (e) {
31905 if (e.target.error) {
31906 Roo.log(e.target.error);
31910 var buffer = e.target.result,
31911 dataView = new DataView(buffer),
31913 maxOffset = dataView.byteLength - 4,
31917 if (dataView.getUint16(0) === 0xffd8) {
31918 while (offset < maxOffset) {
31919 markerBytes = dataView.getUint16(offset);
31921 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31922 markerLength = dataView.getUint16(offset + 2) + 2;
31923 if (offset + markerLength > dataView.byteLength) {
31924 Roo.log('Invalid meta data: Invalid segment size.');
31928 if(markerBytes == 0xffe1){
31929 _this.parseExifData(
31936 offset += markerLength;
31946 var url = _this.urlAPI.createObjectURL(_this.file);
31948 _this.loadCanvas(url);
31953 reader.readAsArrayBuffer(this.file);
31959 parseExifData : function(dataView, offset, length)
31961 var tiffOffset = offset + 10,
31965 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31966 // No Exif data, might be XMP data instead
31970 // Check for the ASCII code for "Exif" (0x45786966):
31971 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31972 // No Exif data, might be XMP data instead
31975 if (tiffOffset + 8 > dataView.byteLength) {
31976 Roo.log('Invalid Exif data: Invalid segment size.');
31979 // Check for the two null bytes:
31980 if (dataView.getUint16(offset + 8) !== 0x0000) {
31981 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31984 // Check the byte alignment:
31985 switch (dataView.getUint16(tiffOffset)) {
31987 littleEndian = true;
31990 littleEndian = false;
31993 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31996 // Check for the TIFF tag marker (0x002A):
31997 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31998 Roo.log('Invalid Exif data: Missing TIFF marker.');
32001 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32002 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32004 this.parseExifTags(
32007 tiffOffset + dirOffset,
32012 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32017 if (dirOffset + 6 > dataView.byteLength) {
32018 Roo.log('Invalid Exif data: Invalid directory offset.');
32021 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32022 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32023 if (dirEndOffset + 4 > dataView.byteLength) {
32024 Roo.log('Invalid Exif data: Invalid directory size.');
32027 for (i = 0; i < tagsNumber; i += 1) {
32031 dirOffset + 2 + 12 * i, // tag offset
32035 // Return the offset to the next directory:
32036 return dataView.getUint32(dirEndOffset, littleEndian);
32039 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32041 var tag = dataView.getUint16(offset, littleEndian);
32043 this.exif[tag] = this.getExifValue(
32047 dataView.getUint16(offset + 2, littleEndian), // tag type
32048 dataView.getUint32(offset + 4, littleEndian), // tag length
32053 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32055 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32064 Roo.log('Invalid Exif data: Invalid tag type.');
32068 tagSize = tagType.size * length;
32069 // Determine if the value is contained in the dataOffset bytes,
32070 // or if the value at the dataOffset is a pointer to the actual data:
32071 dataOffset = tagSize > 4 ?
32072 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32073 if (dataOffset + tagSize > dataView.byteLength) {
32074 Roo.log('Invalid Exif data: Invalid data offset.');
32077 if (length === 1) {
32078 return tagType.getValue(dataView, dataOffset, littleEndian);
32081 for (i = 0; i < length; i += 1) {
32082 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32085 if (tagType.ascii) {
32087 // Concatenate the chars:
32088 for (i = 0; i < values.length; i += 1) {
32090 // Ignore the terminating NULL byte(s):
32091 if (c === '\u0000') {
32103 Roo.apply(Roo.bootstrap.UploadCropbox, {
32105 'Orientation': 0x0112
32109 1: 0, //'top-left',
32111 3: 180, //'bottom-right',
32112 // 4: 'bottom-left',
32114 6: 90, //'right-top',
32115 // 7: 'right-bottom',
32116 8: 270 //'left-bottom'
32120 // byte, 8-bit unsigned int:
32122 getValue: function (dataView, dataOffset) {
32123 return dataView.getUint8(dataOffset);
32127 // ascii, 8-bit byte:
32129 getValue: function (dataView, dataOffset) {
32130 return String.fromCharCode(dataView.getUint8(dataOffset));
32135 // short, 16 bit int:
32137 getValue: function (dataView, dataOffset, littleEndian) {
32138 return dataView.getUint16(dataOffset, littleEndian);
32142 // long, 32 bit int:
32144 getValue: function (dataView, dataOffset, littleEndian) {
32145 return dataView.getUint32(dataOffset, littleEndian);
32149 // rational = two long values, first is numerator, second is denominator:
32151 getValue: function (dataView, dataOffset, littleEndian) {
32152 return dataView.getUint32(dataOffset, littleEndian) /
32153 dataView.getUint32(dataOffset + 4, littleEndian);
32157 // slong, 32 bit signed int:
32159 getValue: function (dataView, dataOffset, littleEndian) {
32160 return dataView.getInt32(dataOffset, littleEndian);
32164 // srational, two slongs, first is numerator, second is denominator:
32166 getValue: function (dataView, dataOffset, littleEndian) {
32167 return dataView.getInt32(dataOffset, littleEndian) /
32168 dataView.getInt32(dataOffset + 4, littleEndian);
32178 cls : 'btn-group roo-upload-cropbox-rotate-left',
32179 action : 'rotate-left',
32183 cls : 'btn btn-default',
32184 html : '<i class="fa fa-undo"></i>'
32190 cls : 'btn-group roo-upload-cropbox-picture',
32191 action : 'picture',
32195 cls : 'btn btn-default',
32196 html : '<i class="fa fa-picture-o"></i>'
32202 cls : 'btn-group roo-upload-cropbox-rotate-right',
32203 action : 'rotate-right',
32207 cls : 'btn btn-default',
32208 html : '<i class="fa fa-repeat"></i>'
32216 cls : 'btn-group roo-upload-cropbox-rotate-left',
32217 action : 'rotate-left',
32221 cls : 'btn btn-default',
32222 html : '<i class="fa fa-undo"></i>'
32228 cls : 'btn-group roo-upload-cropbox-download',
32229 action : 'download',
32233 cls : 'btn btn-default',
32234 html : '<i class="fa fa-download"></i>'
32240 cls : 'btn-group roo-upload-cropbox-crop',
32245 cls : 'btn btn-default',
32246 html : '<i class="fa fa-crop"></i>'
32252 cls : 'btn-group roo-upload-cropbox-trash',
32257 cls : 'btn btn-default',
32258 html : '<i class="fa fa-trash"></i>'
32264 cls : 'btn-group roo-upload-cropbox-rotate-right',
32265 action : 'rotate-right',
32269 cls : 'btn btn-default',
32270 html : '<i class="fa fa-repeat"></i>'
32278 cls : 'btn-group roo-upload-cropbox-rotate-left',
32279 action : 'rotate-left',
32283 cls : 'btn btn-default',
32284 html : '<i class="fa fa-undo"></i>'
32290 cls : 'btn-group roo-upload-cropbox-rotate-right',
32291 action : 'rotate-right',
32295 cls : 'btn btn-default',
32296 html : '<i class="fa fa-repeat"></i>'
32309 * @class Roo.bootstrap.DocumentManager
32310 * @extends Roo.bootstrap.Component
32311 * Bootstrap DocumentManager class
32312 * @cfg {String} paramName default 'imageUpload'
32313 * @cfg {String} toolTipName default 'filename'
32314 * @cfg {String} method default POST
32315 * @cfg {String} url action url
32316 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32317 * @cfg {Boolean} multiple multiple upload default true
32318 * @cfg {Number} thumbSize default 300
32319 * @cfg {String} fieldLabel
32320 * @cfg {Number} labelWidth default 4
32321 * @cfg {String} labelAlign (left|top) default left
32322 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32323 * @cfg {Number} labellg set the width of label (1-12)
32324 * @cfg {Number} labelmd set the width of label (1-12)
32325 * @cfg {Number} labelsm set the width of label (1-12)
32326 * @cfg {Number} labelxs set the width of label (1-12)
32329 * Create a new DocumentManager
32330 * @param {Object} config The config object
32333 Roo.bootstrap.DocumentManager = function(config){
32334 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32337 this.delegates = [];
32342 * Fire when initial the DocumentManager
32343 * @param {Roo.bootstrap.DocumentManager} this
32348 * inspect selected file
32349 * @param {Roo.bootstrap.DocumentManager} this
32350 * @param {File} file
32355 * Fire when xhr load exception
32356 * @param {Roo.bootstrap.DocumentManager} this
32357 * @param {XMLHttpRequest} xhr
32359 "exception" : true,
32361 * @event afterupload
32362 * Fire when xhr load exception
32363 * @param {Roo.bootstrap.DocumentManager} this
32364 * @param {XMLHttpRequest} xhr
32366 "afterupload" : true,
32369 * prepare the form data
32370 * @param {Roo.bootstrap.DocumentManager} this
32371 * @param {Object} formData
32376 * Fire when remove the file
32377 * @param {Roo.bootstrap.DocumentManager} this
32378 * @param {Object} file
32383 * Fire after refresh the file
32384 * @param {Roo.bootstrap.DocumentManager} this
32389 * Fire after click the image
32390 * @param {Roo.bootstrap.DocumentManager} this
32391 * @param {Object} file
32396 * Fire when upload a image and editable set to true
32397 * @param {Roo.bootstrap.DocumentManager} this
32398 * @param {Object} file
32402 * @event beforeselectfile
32403 * Fire before select file
32404 * @param {Roo.bootstrap.DocumentManager} this
32406 "beforeselectfile" : true,
32409 * Fire before process file
32410 * @param {Roo.bootstrap.DocumentManager} this
32411 * @param {Object} file
32415 * @event previewrendered
32416 * Fire when preview rendered
32417 * @param {Roo.bootstrap.DocumentManager} this
32418 * @param {Object} file
32420 "previewrendered" : true,
32423 "previewResize" : true
32428 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32437 paramName : 'imageUpload',
32438 toolTipName : 'filename',
32441 labelAlign : 'left',
32451 getAutoCreate : function()
32453 var managerWidget = {
32455 cls : 'roo-document-manager',
32459 cls : 'roo-document-manager-selector',
32464 cls : 'roo-document-manager-uploader',
32468 cls : 'roo-document-manager-upload-btn',
32469 html : '<i class="fa fa-plus"></i>'
32480 cls : 'column col-md-12',
32485 if(this.fieldLabel.length){
32490 cls : 'column col-md-12',
32491 html : this.fieldLabel
32495 cls : 'column col-md-12',
32500 if(this.labelAlign == 'left'){
32505 html : this.fieldLabel
32514 if(this.labelWidth > 12){
32515 content[0].style = "width: " + this.labelWidth + 'px';
32518 if(this.labelWidth < 13 && this.labelmd == 0){
32519 this.labelmd = this.labelWidth;
32522 if(this.labellg > 0){
32523 content[0].cls += ' col-lg-' + this.labellg;
32524 content[1].cls += ' col-lg-' + (12 - this.labellg);
32527 if(this.labelmd > 0){
32528 content[0].cls += ' col-md-' + this.labelmd;
32529 content[1].cls += ' col-md-' + (12 - this.labelmd);
32532 if(this.labelsm > 0){
32533 content[0].cls += ' col-sm-' + this.labelsm;
32534 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32537 if(this.labelxs > 0){
32538 content[0].cls += ' col-xs-' + this.labelxs;
32539 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32547 cls : 'row clearfix',
32555 initEvents : function()
32557 this.managerEl = this.el.select('.roo-document-manager', true).first();
32558 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32560 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32561 this.selectorEl.hide();
32564 this.selectorEl.attr('multiple', 'multiple');
32567 this.selectorEl.on('change', this.onFileSelected, this);
32569 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32570 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32572 this.uploader.on('click', this.onUploaderClick, this);
32574 this.renderProgressDialog();
32578 window.addEventListener("resize", function() { _this.refresh(); } );
32580 this.fireEvent('initial', this);
32583 renderProgressDialog : function()
32587 this.progressDialog = new Roo.bootstrap.Modal({
32588 cls : 'roo-document-manager-progress-dialog',
32589 allow_close : false,
32600 btnclick : function() {
32601 _this.uploadCancel();
32607 this.progressDialog.render(Roo.get(document.body));
32609 this.progress = new Roo.bootstrap.Progress({
32610 cls : 'roo-document-manager-progress',
32615 this.progress.render(this.progressDialog.getChildContainer());
32617 this.progressBar = new Roo.bootstrap.ProgressBar({
32618 cls : 'roo-document-manager-progress-bar',
32621 aria_valuemax : 12,
32625 this.progressBar.render(this.progress.getChildContainer());
32628 onUploaderClick : function(e)
32630 e.preventDefault();
32632 if(this.fireEvent('beforeselectfile', this) != false){
32633 this.selectorEl.dom.click();
32638 onFileSelected : function(e)
32640 e.preventDefault();
32642 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32646 Roo.each(this.selectorEl.dom.files, function(file){
32647 if(this.fireEvent('inspect', this, file) != false){
32648 this.files.push(file);
32658 this.selectorEl.dom.value = '';
32660 if(!this.files || !this.files.length){
32664 if(this.boxes > 0 && this.files.length > this.boxes){
32665 this.files = this.files.slice(0, this.boxes);
32668 this.uploader.show();
32670 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32671 this.uploader.hide();
32680 Roo.each(this.files, function(file){
32682 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32683 var f = this.renderPreview(file);
32688 if(file.type.indexOf('image') != -1){
32689 this.delegates.push(
32691 _this.process(file);
32692 }).createDelegate(this)
32700 _this.process(file);
32701 }).createDelegate(this)
32706 this.files = files;
32708 this.delegates = this.delegates.concat(docs);
32710 if(!this.delegates.length){
32715 this.progressBar.aria_valuemax = this.delegates.length;
32722 arrange : function()
32724 if(!this.delegates.length){
32725 this.progressDialog.hide();
32730 var delegate = this.delegates.shift();
32732 this.progressDialog.show();
32734 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32736 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32741 refresh : function()
32743 this.uploader.show();
32745 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32746 this.uploader.hide();
32749 Roo.isTouch ? this.closable(false) : this.closable(true);
32751 this.fireEvent('refresh', this);
32754 onRemove : function(e, el, o)
32756 e.preventDefault();
32758 this.fireEvent('remove', this, o);
32762 remove : function(o)
32766 Roo.each(this.files, function(file){
32767 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32776 this.files = files;
32783 Roo.each(this.files, function(file){
32788 file.target.remove();
32797 onClick : function(e, el, o)
32799 e.preventDefault();
32801 this.fireEvent('click', this, o);
32805 closable : function(closable)
32807 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32809 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32821 xhrOnLoad : function(xhr)
32823 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32827 if (xhr.readyState !== 4) {
32829 this.fireEvent('exception', this, xhr);
32833 var response = Roo.decode(xhr.responseText);
32835 if(!response.success){
32837 this.fireEvent('exception', this, xhr);
32841 var file = this.renderPreview(response.data);
32843 this.files.push(file);
32847 this.fireEvent('afterupload', this, xhr);
32851 xhrOnError : function(xhr)
32853 Roo.log('xhr on error');
32855 var response = Roo.decode(xhr.responseText);
32862 process : function(file)
32864 if(this.fireEvent('process', this, file) !== false){
32865 if(this.editable && file.type.indexOf('image') != -1){
32866 this.fireEvent('edit', this, file);
32870 this.uploadStart(file, false);
32877 uploadStart : function(file, crop)
32879 this.xhr = new XMLHttpRequest();
32881 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32886 file.xhr = this.xhr;
32888 this.managerEl.createChild({
32890 cls : 'roo-document-manager-loading',
32894 tooltip : file.name,
32895 cls : 'roo-document-manager-thumb',
32896 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32902 this.xhr.open(this.method, this.url, true);
32905 "Accept": "application/json",
32906 "Cache-Control": "no-cache",
32907 "X-Requested-With": "XMLHttpRequest"
32910 for (var headerName in headers) {
32911 var headerValue = headers[headerName];
32913 this.xhr.setRequestHeader(headerName, headerValue);
32919 this.xhr.onload = function()
32921 _this.xhrOnLoad(_this.xhr);
32924 this.xhr.onerror = function()
32926 _this.xhrOnError(_this.xhr);
32929 var formData = new FormData();
32931 formData.append('returnHTML', 'NO');
32934 formData.append('crop', crop);
32937 formData.append(this.paramName, file, file.name);
32944 if(this.fireEvent('prepare', this, formData, options) != false){
32946 if(options.manually){
32950 this.xhr.send(formData);
32954 this.uploadCancel();
32957 uploadCancel : function()
32963 this.delegates = [];
32965 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32972 renderPreview : function(file)
32974 if(typeof(file.target) != 'undefined' && file.target){
32978 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32980 var previewEl = this.managerEl.createChild({
32982 cls : 'roo-document-manager-preview',
32986 tooltip : file[this.toolTipName],
32987 cls : 'roo-document-manager-thumb',
32988 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32993 html : '<i class="fa fa-times-circle"></i>'
32998 var close = previewEl.select('button.close', true).first();
33000 close.on('click', this.onRemove, this, file);
33002 file.target = previewEl;
33004 var image = previewEl.select('img', true).first();
33008 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33010 image.on('click', this.onClick, this, file);
33012 this.fireEvent('previewrendered', this, file);
33018 onPreviewLoad : function(file, image)
33020 if(typeof(file.target) == 'undefined' || !file.target){
33024 var width = image.dom.naturalWidth || image.dom.width;
33025 var height = image.dom.naturalHeight || image.dom.height;
33027 if(!this.previewResize) {
33031 if(width > height){
33032 file.target.addClass('wide');
33036 file.target.addClass('tall');
33041 uploadFromSource : function(file, crop)
33043 this.xhr = new XMLHttpRequest();
33045 this.managerEl.createChild({
33047 cls : 'roo-document-manager-loading',
33051 tooltip : file.name,
33052 cls : 'roo-document-manager-thumb',
33053 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33059 this.xhr.open(this.method, this.url, true);
33062 "Accept": "application/json",
33063 "Cache-Control": "no-cache",
33064 "X-Requested-With": "XMLHttpRequest"
33067 for (var headerName in headers) {
33068 var headerValue = headers[headerName];
33070 this.xhr.setRequestHeader(headerName, headerValue);
33076 this.xhr.onload = function()
33078 _this.xhrOnLoad(_this.xhr);
33081 this.xhr.onerror = function()
33083 _this.xhrOnError(_this.xhr);
33086 var formData = new FormData();
33088 formData.append('returnHTML', 'NO');
33090 formData.append('crop', crop);
33092 if(typeof(file.filename) != 'undefined'){
33093 formData.append('filename', file.filename);
33096 if(typeof(file.mimetype) != 'undefined'){
33097 formData.append('mimetype', file.mimetype);
33102 if(this.fireEvent('prepare', this, formData) != false){
33103 this.xhr.send(formData);
33113 * @class Roo.bootstrap.DocumentViewer
33114 * @extends Roo.bootstrap.Component
33115 * Bootstrap DocumentViewer class
33116 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33117 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33120 * Create a new DocumentViewer
33121 * @param {Object} config The config object
33124 Roo.bootstrap.DocumentViewer = function(config){
33125 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33130 * Fire after initEvent
33131 * @param {Roo.bootstrap.DocumentViewer} this
33137 * @param {Roo.bootstrap.DocumentViewer} this
33142 * Fire after download button
33143 * @param {Roo.bootstrap.DocumentViewer} this
33148 * Fire after trash button
33149 * @param {Roo.bootstrap.DocumentViewer} this
33156 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33158 showDownload : true,
33162 getAutoCreate : function()
33166 cls : 'roo-document-viewer',
33170 cls : 'roo-document-viewer-body',
33174 cls : 'roo-document-viewer-thumb',
33178 cls : 'roo-document-viewer-image'
33186 cls : 'roo-document-viewer-footer',
33189 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33193 cls : 'btn-group roo-document-viewer-download',
33197 cls : 'btn btn-default',
33198 html : '<i class="fa fa-download"></i>'
33204 cls : 'btn-group roo-document-viewer-trash',
33208 cls : 'btn btn-default',
33209 html : '<i class="fa fa-trash"></i>'
33222 initEvents : function()
33224 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33225 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33227 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33228 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33230 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33231 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33233 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33234 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33236 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33237 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33239 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33240 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33242 this.bodyEl.on('click', this.onClick, this);
33243 this.downloadBtn.on('click', this.onDownload, this);
33244 this.trashBtn.on('click', this.onTrash, this);
33246 this.downloadBtn.hide();
33247 this.trashBtn.hide();
33249 if(this.showDownload){
33250 this.downloadBtn.show();
33253 if(this.showTrash){
33254 this.trashBtn.show();
33257 if(!this.showDownload && !this.showTrash) {
33258 this.footerEl.hide();
33263 initial : function()
33265 this.fireEvent('initial', this);
33269 onClick : function(e)
33271 e.preventDefault();
33273 this.fireEvent('click', this);
33276 onDownload : function(e)
33278 e.preventDefault();
33280 this.fireEvent('download', this);
33283 onTrash : function(e)
33285 e.preventDefault();
33287 this.fireEvent('trash', this);
33299 * @class Roo.bootstrap.NavProgressBar
33300 * @extends Roo.bootstrap.Component
33301 * Bootstrap NavProgressBar class
33304 * Create a new nav progress bar
33305 * @param {Object} config The config object
33308 Roo.bootstrap.NavProgressBar = function(config){
33309 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33311 this.bullets = this.bullets || [];
33313 // Roo.bootstrap.NavProgressBar.register(this);
33317 * Fires when the active item changes
33318 * @param {Roo.bootstrap.NavProgressBar} this
33319 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33320 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33327 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33332 getAutoCreate : function()
33334 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33338 cls : 'roo-navigation-bar-group',
33342 cls : 'roo-navigation-top-bar'
33346 cls : 'roo-navigation-bullets-bar',
33350 cls : 'roo-navigation-bar'
33357 cls : 'roo-navigation-bottom-bar'
33367 initEvents: function()
33372 onRender : function(ct, position)
33374 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33376 if(this.bullets.length){
33377 Roo.each(this.bullets, function(b){
33386 addItem : function(cfg)
33388 var item = new Roo.bootstrap.NavProgressItem(cfg);
33390 item.parentId = this.id;
33391 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33394 var top = new Roo.bootstrap.Element({
33396 cls : 'roo-navigation-bar-text'
33399 var bottom = new Roo.bootstrap.Element({
33401 cls : 'roo-navigation-bar-text'
33404 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33405 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33407 var topText = new Roo.bootstrap.Element({
33409 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33412 var bottomText = new Roo.bootstrap.Element({
33414 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33417 topText.onRender(top.el, null);
33418 bottomText.onRender(bottom.el, null);
33421 item.bottomEl = bottom;
33424 this.barItems.push(item);
33429 getActive : function()
33431 var active = false;
33433 Roo.each(this.barItems, function(v){
33435 if (!v.isActive()) {
33447 setActiveItem : function(item)
33451 Roo.each(this.barItems, function(v){
33452 if (v.rid == item.rid) {
33456 if (v.isActive()) {
33457 v.setActive(false);
33462 item.setActive(true);
33464 this.fireEvent('changed', this, item, prev);
33467 getBarItem: function(rid)
33471 Roo.each(this.barItems, function(e) {
33472 if (e.rid != rid) {
33483 indexOfItem : function(item)
33487 Roo.each(this.barItems, function(v, i){
33489 if (v.rid != item.rid) {
33500 setActiveNext : function()
33502 var i = this.indexOfItem(this.getActive());
33504 if (i > this.barItems.length) {
33508 this.setActiveItem(this.barItems[i+1]);
33511 setActivePrev : function()
33513 var i = this.indexOfItem(this.getActive());
33519 this.setActiveItem(this.barItems[i-1]);
33522 format : function()
33524 if(!this.barItems.length){
33528 var width = 100 / this.barItems.length;
33530 Roo.each(this.barItems, function(i){
33531 i.el.setStyle('width', width + '%');
33532 i.topEl.el.setStyle('width', width + '%');
33533 i.bottomEl.el.setStyle('width', width + '%');
33542 * Nav Progress Item
33547 * @class Roo.bootstrap.NavProgressItem
33548 * @extends Roo.bootstrap.Component
33549 * Bootstrap NavProgressItem class
33550 * @cfg {String} rid the reference id
33551 * @cfg {Boolean} active (true|false) Is item active default false
33552 * @cfg {Boolean} disabled (true|false) Is item active default false
33553 * @cfg {String} html
33554 * @cfg {String} position (top|bottom) text position default bottom
33555 * @cfg {String} icon show icon instead of number
33558 * Create a new NavProgressItem
33559 * @param {Object} config The config object
33561 Roo.bootstrap.NavProgressItem = function(config){
33562 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33567 * The raw click event for the entire grid.
33568 * @param {Roo.bootstrap.NavProgressItem} this
33569 * @param {Roo.EventObject} e
33576 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33582 position : 'bottom',
33585 getAutoCreate : function()
33587 var iconCls = 'roo-navigation-bar-item-icon';
33589 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33593 cls: 'roo-navigation-bar-item',
33603 cfg.cls += ' active';
33606 cfg.cls += ' disabled';
33612 disable : function()
33614 this.setDisabled(true);
33617 enable : function()
33619 this.setDisabled(false);
33622 initEvents: function()
33624 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33626 this.iconEl.on('click', this.onClick, this);
33629 onClick : function(e)
33631 e.preventDefault();
33637 if(this.fireEvent('click', this, e) === false){
33641 this.parent().setActiveItem(this);
33644 isActive: function ()
33646 return this.active;
33649 setActive : function(state)
33651 if(this.active == state){
33655 this.active = state;
33658 this.el.addClass('active');
33662 this.el.removeClass('active');
33667 setDisabled : function(state)
33669 if(this.disabled == state){
33673 this.disabled = state;
33676 this.el.addClass('disabled');
33680 this.el.removeClass('disabled');
33683 tooltipEl : function()
33685 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33698 * @class Roo.bootstrap.FieldLabel
33699 * @extends Roo.bootstrap.Component
33700 * Bootstrap FieldLabel class
33701 * @cfg {String} html contents of the element
33702 * @cfg {String} tag tag of the element default label
33703 * @cfg {String} cls class of the element
33704 * @cfg {String} target label target
33705 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33706 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33707 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33708 * @cfg {String} iconTooltip default "This field is required"
33709 * @cfg {String} indicatorpos (left|right) default left
33712 * Create a new FieldLabel
33713 * @param {Object} config The config object
33716 Roo.bootstrap.FieldLabel = function(config){
33717 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33722 * Fires after the field has been marked as invalid.
33723 * @param {Roo.form.FieldLabel} this
33724 * @param {String} msg The validation message
33729 * Fires after the field has been validated with no errors.
33730 * @param {Roo.form.FieldLabel} this
33736 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33743 invalidClass : 'has-warning',
33744 validClass : 'has-success',
33745 iconTooltip : 'This field is required',
33746 indicatorpos : 'left',
33748 getAutoCreate : function(){
33751 if (!this.allowBlank) {
33757 cls : 'roo-bootstrap-field-label ' + this.cls,
33762 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33763 tooltip : this.iconTooltip
33772 if(this.indicatorpos == 'right'){
33775 cls : 'roo-bootstrap-field-label ' + this.cls,
33784 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33785 tooltip : this.iconTooltip
33794 initEvents: function()
33796 Roo.bootstrap.Element.superclass.initEvents.call(this);
33798 this.indicator = this.indicatorEl();
33800 if(this.indicator){
33801 this.indicator.removeClass('visible');
33802 this.indicator.addClass('invisible');
33805 Roo.bootstrap.FieldLabel.register(this);
33808 indicatorEl : function()
33810 var indicator = this.el.select('i.roo-required-indicator',true).first();
33821 * Mark this field as valid
33823 markValid : function()
33825 if(this.indicator){
33826 this.indicator.removeClass('visible');
33827 this.indicator.addClass('invisible');
33829 if (Roo.bootstrap.version == 3) {
33830 this.el.removeClass(this.invalidClass);
33831 this.el.addClass(this.validClass);
33833 this.el.removeClass('is-invalid');
33834 this.el.addClass('is-valid');
33838 this.fireEvent('valid', this);
33842 * Mark this field as invalid
33843 * @param {String} msg The validation message
33845 markInvalid : function(msg)
33847 if(this.indicator){
33848 this.indicator.removeClass('invisible');
33849 this.indicator.addClass('visible');
33851 if (Roo.bootstrap.version == 3) {
33852 this.el.removeClass(this.validClass);
33853 this.el.addClass(this.invalidClass);
33855 this.el.removeClass('is-valid');
33856 this.el.addClass('is-invalid');
33860 this.fireEvent('invalid', this, msg);
33866 Roo.apply(Roo.bootstrap.FieldLabel, {
33871 * register a FieldLabel Group
33872 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33874 register : function(label)
33876 if(this.groups.hasOwnProperty(label.target)){
33880 this.groups[label.target] = label;
33884 * fetch a FieldLabel Group based on the target
33885 * @param {string} target
33886 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33888 get: function(target) {
33889 if (typeof(this.groups[target]) == 'undefined') {
33893 return this.groups[target] ;
33902 * page DateSplitField.
33908 * @class Roo.bootstrap.DateSplitField
33909 * @extends Roo.bootstrap.Component
33910 * Bootstrap DateSplitField class
33911 * @cfg {string} fieldLabel - the label associated
33912 * @cfg {Number} labelWidth set the width of label (0-12)
33913 * @cfg {String} labelAlign (top|left)
33914 * @cfg {Boolean} dayAllowBlank (true|false) default false
33915 * @cfg {Boolean} monthAllowBlank (true|false) default false
33916 * @cfg {Boolean} yearAllowBlank (true|false) default false
33917 * @cfg {string} dayPlaceholder
33918 * @cfg {string} monthPlaceholder
33919 * @cfg {string} yearPlaceholder
33920 * @cfg {string} dayFormat default 'd'
33921 * @cfg {string} monthFormat default 'm'
33922 * @cfg {string} yearFormat default 'Y'
33923 * @cfg {Number} labellg set the width of label (1-12)
33924 * @cfg {Number} labelmd set the width of label (1-12)
33925 * @cfg {Number} labelsm set the width of label (1-12)
33926 * @cfg {Number} labelxs set the width of label (1-12)
33930 * Create a new DateSplitField
33931 * @param {Object} config The config object
33934 Roo.bootstrap.DateSplitField = function(config){
33935 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33941 * getting the data of years
33942 * @param {Roo.bootstrap.DateSplitField} this
33943 * @param {Object} years
33948 * getting the data of days
33949 * @param {Roo.bootstrap.DateSplitField} this
33950 * @param {Object} days
33955 * Fires after the field has been marked as invalid.
33956 * @param {Roo.form.Field} this
33957 * @param {String} msg The validation message
33962 * Fires after the field has been validated with no errors.
33963 * @param {Roo.form.Field} this
33969 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33972 labelAlign : 'top',
33974 dayAllowBlank : false,
33975 monthAllowBlank : false,
33976 yearAllowBlank : false,
33977 dayPlaceholder : '',
33978 monthPlaceholder : '',
33979 yearPlaceholder : '',
33983 isFormField : true,
33989 getAutoCreate : function()
33993 cls : 'row roo-date-split-field-group',
33998 cls : 'form-hidden-field roo-date-split-field-group-value',
34004 var labelCls = 'col-md-12';
34005 var contentCls = 'col-md-4';
34007 if(this.fieldLabel){
34011 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34015 html : this.fieldLabel
34020 if(this.labelAlign == 'left'){
34022 if(this.labelWidth > 12){
34023 label.style = "width: " + this.labelWidth + 'px';
34026 if(this.labelWidth < 13 && this.labelmd == 0){
34027 this.labelmd = this.labelWidth;
34030 if(this.labellg > 0){
34031 labelCls = ' col-lg-' + this.labellg;
34032 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34035 if(this.labelmd > 0){
34036 labelCls = ' col-md-' + this.labelmd;
34037 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34040 if(this.labelsm > 0){
34041 labelCls = ' col-sm-' + this.labelsm;
34042 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34045 if(this.labelxs > 0){
34046 labelCls = ' col-xs-' + this.labelxs;
34047 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34051 label.cls += ' ' + labelCls;
34053 cfg.cn.push(label);
34056 Roo.each(['day', 'month', 'year'], function(t){
34059 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34066 inputEl: function ()
34068 return this.el.select('.roo-date-split-field-group-value', true).first();
34071 onRender : function(ct, position)
34075 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34077 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34079 this.dayField = new Roo.bootstrap.ComboBox({
34080 allowBlank : this.dayAllowBlank,
34081 alwaysQuery : true,
34082 displayField : 'value',
34085 forceSelection : true,
34087 placeholder : this.dayPlaceholder,
34088 selectOnFocus : true,
34089 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34090 triggerAction : 'all',
34092 valueField : 'value',
34093 store : new Roo.data.SimpleStore({
34094 data : (function() {
34096 _this.fireEvent('days', _this, days);
34099 fields : [ 'value' ]
34102 select : function (_self, record, index)
34104 _this.setValue(_this.getValue());
34109 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34111 this.monthField = new Roo.bootstrap.MonthField({
34112 after : '<i class=\"fa fa-calendar\"></i>',
34113 allowBlank : this.monthAllowBlank,
34114 placeholder : this.monthPlaceholder,
34117 render : function (_self)
34119 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34120 e.preventDefault();
34124 select : function (_self, oldvalue, newvalue)
34126 _this.setValue(_this.getValue());
34131 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34133 this.yearField = new Roo.bootstrap.ComboBox({
34134 allowBlank : this.yearAllowBlank,
34135 alwaysQuery : true,
34136 displayField : 'value',
34139 forceSelection : true,
34141 placeholder : this.yearPlaceholder,
34142 selectOnFocus : true,
34143 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34144 triggerAction : 'all',
34146 valueField : 'value',
34147 store : new Roo.data.SimpleStore({
34148 data : (function() {
34150 _this.fireEvent('years', _this, years);
34153 fields : [ 'value' ]
34156 select : function (_self, record, index)
34158 _this.setValue(_this.getValue());
34163 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34166 setValue : function(v, format)
34168 this.inputEl.dom.value = v;
34170 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34172 var d = Date.parseDate(v, f);
34179 this.setDay(d.format(this.dayFormat));
34180 this.setMonth(d.format(this.monthFormat));
34181 this.setYear(d.format(this.yearFormat));
34188 setDay : function(v)
34190 this.dayField.setValue(v);
34191 this.inputEl.dom.value = this.getValue();
34196 setMonth : function(v)
34198 this.monthField.setValue(v, true);
34199 this.inputEl.dom.value = this.getValue();
34204 setYear : function(v)
34206 this.yearField.setValue(v);
34207 this.inputEl.dom.value = this.getValue();
34212 getDay : function()
34214 return this.dayField.getValue();
34217 getMonth : function()
34219 return this.monthField.getValue();
34222 getYear : function()
34224 return this.yearField.getValue();
34227 getValue : function()
34229 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34231 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34241 this.inputEl.dom.value = '';
34246 validate : function()
34248 var d = this.dayField.validate();
34249 var m = this.monthField.validate();
34250 var y = this.yearField.validate();
34255 (!this.dayAllowBlank && !d) ||
34256 (!this.monthAllowBlank && !m) ||
34257 (!this.yearAllowBlank && !y)
34262 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34271 this.markInvalid();
34276 markValid : function()
34279 var label = this.el.select('label', true).first();
34280 var icon = this.el.select('i.fa-star', true).first();
34286 this.fireEvent('valid', this);
34290 * Mark this field as invalid
34291 * @param {String} msg The validation message
34293 markInvalid : function(msg)
34296 var label = this.el.select('label', true).first();
34297 var icon = this.el.select('i.fa-star', true).first();
34299 if(label && !icon){
34300 this.el.select('.roo-date-split-field-label', true).createChild({
34302 cls : 'text-danger fa fa-lg fa-star',
34303 tooltip : 'This field is required',
34304 style : 'margin-right:5px;'
34308 this.fireEvent('invalid', this, msg);
34311 clearInvalid : function()
34313 var label = this.el.select('label', true).first();
34314 var icon = this.el.select('i.fa-star', true).first();
34320 this.fireEvent('valid', this);
34323 getName: function()
34333 * http://masonry.desandro.com
34335 * The idea is to render all the bricks based on vertical width...
34337 * The original code extends 'outlayer' - we might need to use that....
34343 * @class Roo.bootstrap.LayoutMasonry
34344 * @extends Roo.bootstrap.Component
34345 * Bootstrap Layout Masonry class
34348 * Create a new Element
34349 * @param {Object} config The config object
34352 Roo.bootstrap.LayoutMasonry = function(config){
34354 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34358 Roo.bootstrap.LayoutMasonry.register(this);
34364 * Fire after layout the items
34365 * @param {Roo.bootstrap.LayoutMasonry} this
34366 * @param {Roo.EventObject} e
34373 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34376 * @cfg {Boolean} isLayoutInstant = no animation?
34378 isLayoutInstant : false, // needed?
34381 * @cfg {Number} boxWidth width of the columns
34386 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34391 * @cfg {Number} padWidth padding below box..
34396 * @cfg {Number} gutter gutter width..
34401 * @cfg {Number} maxCols maximum number of columns
34407 * @cfg {Boolean} isAutoInitial defalut true
34409 isAutoInitial : true,
34414 * @cfg {Boolean} isHorizontal defalut false
34416 isHorizontal : false,
34418 currentSize : null,
34424 bricks: null, //CompositeElement
34428 _isLayoutInited : false,
34430 // isAlternative : false, // only use for vertical layout...
34433 * @cfg {Number} alternativePadWidth padding below box..
34435 alternativePadWidth : 50,
34437 selectedBrick : [],
34439 getAutoCreate : function(){
34441 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34445 cls: 'blog-masonary-wrapper ' + this.cls,
34447 cls : 'mas-boxes masonary'
34454 getChildContainer: function( )
34456 if (this.boxesEl) {
34457 return this.boxesEl;
34460 this.boxesEl = this.el.select('.mas-boxes').first();
34462 return this.boxesEl;
34466 initEvents : function()
34470 if(this.isAutoInitial){
34471 Roo.log('hook children rendered');
34472 this.on('childrenrendered', function() {
34473 Roo.log('children rendered');
34479 initial : function()
34481 this.selectedBrick = [];
34483 this.currentSize = this.el.getBox(true);
34485 Roo.EventManager.onWindowResize(this.resize, this);
34487 if(!this.isAutoInitial){
34495 //this.layout.defer(500,this);
34499 resize : function()
34501 var cs = this.el.getBox(true);
34504 this.currentSize.width == cs.width &&
34505 this.currentSize.x == cs.x &&
34506 this.currentSize.height == cs.height &&
34507 this.currentSize.y == cs.y
34509 Roo.log("no change in with or X or Y");
34513 this.currentSize = cs;
34519 layout : function()
34521 this._resetLayout();
34523 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34525 this.layoutItems( isInstant );
34527 this._isLayoutInited = true;
34529 this.fireEvent('layout', this);
34533 _resetLayout : function()
34535 if(this.isHorizontal){
34536 this.horizontalMeasureColumns();
34540 this.verticalMeasureColumns();
34544 verticalMeasureColumns : function()
34546 this.getContainerWidth();
34548 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34549 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34553 var boxWidth = this.boxWidth + this.padWidth;
34555 if(this.containerWidth < this.boxWidth){
34556 boxWidth = this.containerWidth
34559 var containerWidth = this.containerWidth;
34561 var cols = Math.floor(containerWidth / boxWidth);
34563 this.cols = Math.max( cols, 1 );
34565 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34567 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34569 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34571 this.colWidth = boxWidth + avail - this.padWidth;
34573 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34574 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34577 horizontalMeasureColumns : function()
34579 this.getContainerWidth();
34581 var boxWidth = this.boxWidth;
34583 if(this.containerWidth < boxWidth){
34584 boxWidth = this.containerWidth;
34587 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34589 this.el.setHeight(boxWidth);
34593 getContainerWidth : function()
34595 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34598 layoutItems : function( isInstant )
34600 Roo.log(this.bricks);
34602 var items = Roo.apply([], this.bricks);
34604 if(this.isHorizontal){
34605 this._horizontalLayoutItems( items , isInstant );
34609 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34610 // this._verticalAlternativeLayoutItems( items , isInstant );
34614 this._verticalLayoutItems( items , isInstant );
34618 _verticalLayoutItems : function ( items , isInstant)
34620 if ( !items || !items.length ) {
34625 ['xs', 'xs', 'xs', 'tall'],
34626 ['xs', 'xs', 'tall'],
34627 ['xs', 'xs', 'sm'],
34628 ['xs', 'xs', 'xs'],
34634 ['sm', 'xs', 'xs'],
34638 ['tall', 'xs', 'xs', 'xs'],
34639 ['tall', 'xs', 'xs'],
34651 Roo.each(items, function(item, k){
34653 switch (item.size) {
34654 // these layouts take up a full box,
34665 boxes.push([item]);
34688 var filterPattern = function(box, length)
34696 var pattern = box.slice(0, length);
34700 Roo.each(pattern, function(i){
34701 format.push(i.size);
34704 Roo.each(standard, function(s){
34706 if(String(s) != String(format)){
34715 if(!match && length == 1){
34720 filterPattern(box, length - 1);
34724 queue.push(pattern);
34726 box = box.slice(length, box.length);
34728 filterPattern(box, 4);
34734 Roo.each(boxes, function(box, k){
34740 if(box.length == 1){
34745 filterPattern(box, 4);
34749 this._processVerticalLayoutQueue( queue, isInstant );
34753 // _verticalAlternativeLayoutItems : function( items , isInstant )
34755 // if ( !items || !items.length ) {
34759 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34763 _horizontalLayoutItems : function ( items , isInstant)
34765 if ( !items || !items.length || items.length < 3) {
34771 var eItems = items.slice(0, 3);
34773 items = items.slice(3, items.length);
34776 ['xs', 'xs', 'xs', 'wide'],
34777 ['xs', 'xs', 'wide'],
34778 ['xs', 'xs', 'sm'],
34779 ['xs', 'xs', 'xs'],
34785 ['sm', 'xs', 'xs'],
34789 ['wide', 'xs', 'xs', 'xs'],
34790 ['wide', 'xs', 'xs'],
34803 Roo.each(items, function(item, k){
34805 switch (item.size) {
34816 boxes.push([item]);
34840 var filterPattern = function(box, length)
34848 var pattern = box.slice(0, length);
34852 Roo.each(pattern, function(i){
34853 format.push(i.size);
34856 Roo.each(standard, function(s){
34858 if(String(s) != String(format)){
34867 if(!match && length == 1){
34872 filterPattern(box, length - 1);
34876 queue.push(pattern);
34878 box = box.slice(length, box.length);
34880 filterPattern(box, 4);
34886 Roo.each(boxes, function(box, k){
34892 if(box.length == 1){
34897 filterPattern(box, 4);
34904 var pos = this.el.getBox(true);
34908 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34910 var hit_end = false;
34912 Roo.each(queue, function(box){
34916 Roo.each(box, function(b){
34918 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34928 Roo.each(box, function(b){
34930 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34933 mx = Math.max(mx, b.x);
34937 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34941 Roo.each(box, function(b){
34943 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34957 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34960 /** Sets position of item in DOM
34961 * @param {Element} item
34962 * @param {Number} x - horizontal position
34963 * @param {Number} y - vertical position
34964 * @param {Boolean} isInstant - disables transitions
34966 _processVerticalLayoutQueue : function( queue, isInstant )
34968 var pos = this.el.getBox(true);
34973 for (var i = 0; i < this.cols; i++){
34977 Roo.each(queue, function(box, k){
34979 var col = k % this.cols;
34981 Roo.each(box, function(b,kk){
34983 b.el.position('absolute');
34985 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34986 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34988 if(b.size == 'md-left' || b.size == 'md-right'){
34989 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34990 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34993 b.el.setWidth(width);
34994 b.el.setHeight(height);
34996 b.el.select('iframe',true).setSize(width,height);
35000 for (var i = 0; i < this.cols; i++){
35002 if(maxY[i] < maxY[col]){
35007 col = Math.min(col, i);
35011 x = pos.x + col * (this.colWidth + this.padWidth);
35015 var positions = [];
35017 switch (box.length){
35019 positions = this.getVerticalOneBoxColPositions(x, y, box);
35022 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35025 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35028 positions = this.getVerticalFourBoxColPositions(x, y, box);
35034 Roo.each(box, function(b,kk){
35036 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35038 var sz = b.el.getSize();
35040 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35048 for (var i = 0; i < this.cols; i++){
35049 mY = Math.max(mY, maxY[i]);
35052 this.el.setHeight(mY - pos.y);
35056 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35058 // var pos = this.el.getBox(true);
35061 // var maxX = pos.right;
35063 // var maxHeight = 0;
35065 // Roo.each(items, function(item, k){
35069 // item.el.position('absolute');
35071 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35073 // item.el.setWidth(width);
35075 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35077 // item.el.setHeight(height);
35080 // item.el.setXY([x, y], isInstant ? false : true);
35082 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35085 // y = y + height + this.alternativePadWidth;
35087 // maxHeight = maxHeight + height + this.alternativePadWidth;
35091 // this.el.setHeight(maxHeight);
35095 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35097 var pos = this.el.getBox(true);
35102 var maxX = pos.right;
35104 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35106 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35108 Roo.each(queue, function(box, k){
35110 Roo.each(box, function(b, kk){
35112 b.el.position('absolute');
35114 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35115 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35117 if(b.size == 'md-left' || b.size == 'md-right'){
35118 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35119 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35122 b.el.setWidth(width);
35123 b.el.setHeight(height);
35131 var positions = [];
35133 switch (box.length){
35135 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35138 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35141 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35144 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35150 Roo.each(box, function(b,kk){
35152 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35154 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35162 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35164 Roo.each(eItems, function(b,k){
35166 b.size = (k == 0) ? 'sm' : 'xs';
35167 b.x = (k == 0) ? 2 : 1;
35168 b.y = (k == 0) ? 2 : 1;
35170 b.el.position('absolute');
35172 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35174 b.el.setWidth(width);
35176 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35178 b.el.setHeight(height);
35182 var positions = [];
35185 x : maxX - this.unitWidth * 2 - this.gutter,
35190 x : maxX - this.unitWidth,
35191 y : minY + (this.unitWidth + this.gutter) * 2
35195 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35199 Roo.each(eItems, function(b,k){
35201 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35207 getVerticalOneBoxColPositions : function(x, y, box)
35211 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35213 if(box[0].size == 'md-left'){
35217 if(box[0].size == 'md-right'){
35222 x : x + (this.unitWidth + this.gutter) * rand,
35229 getVerticalTwoBoxColPositions : function(x, y, box)
35233 if(box[0].size == 'xs'){
35237 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35241 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35255 x : x + (this.unitWidth + this.gutter) * 2,
35256 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35263 getVerticalThreeBoxColPositions : function(x, y, box)
35267 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35275 x : x + (this.unitWidth + this.gutter) * 1,
35280 x : x + (this.unitWidth + this.gutter) * 2,
35288 if(box[0].size == 'xs' && box[1].size == 'xs'){
35297 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35301 x : x + (this.unitWidth + this.gutter) * 1,
35315 x : x + (this.unitWidth + this.gutter) * 2,
35320 x : x + (this.unitWidth + this.gutter) * 2,
35321 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35328 getVerticalFourBoxColPositions : function(x, y, box)
35332 if(box[0].size == 'xs'){
35341 y : y + (this.unitHeight + this.gutter) * 1
35346 y : y + (this.unitHeight + this.gutter) * 2
35350 x : x + (this.unitWidth + this.gutter) * 1,
35364 x : x + (this.unitWidth + this.gutter) * 2,
35369 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35370 y : y + (this.unitHeight + this.gutter) * 1
35374 x : x + (this.unitWidth + this.gutter) * 2,
35375 y : y + (this.unitWidth + this.gutter) * 2
35382 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35386 if(box[0].size == 'md-left'){
35388 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35395 if(box[0].size == 'md-right'){
35397 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35398 y : minY + (this.unitWidth + this.gutter) * 1
35404 var rand = Math.floor(Math.random() * (4 - box[0].y));
35407 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35408 y : minY + (this.unitWidth + this.gutter) * rand
35415 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35419 if(box[0].size == 'xs'){
35422 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35427 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35428 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35436 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35441 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35442 y : minY + (this.unitWidth + this.gutter) * 2
35449 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35453 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35456 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35461 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35462 y : minY + (this.unitWidth + this.gutter) * 1
35466 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35467 y : minY + (this.unitWidth + this.gutter) * 2
35474 if(box[0].size == 'xs' && box[1].size == 'xs'){
35477 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35482 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35487 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35488 y : minY + (this.unitWidth + this.gutter) * 1
35496 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35501 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35502 y : minY + (this.unitWidth + this.gutter) * 2
35506 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35507 y : minY + (this.unitWidth + this.gutter) * 2
35514 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35518 if(box[0].size == 'xs'){
35521 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35526 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35531 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),
35536 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35537 y : minY + (this.unitWidth + this.gutter) * 1
35545 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35550 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35551 y : minY + (this.unitWidth + this.gutter) * 2
35555 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35556 y : minY + (this.unitWidth + this.gutter) * 2
35560 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),
35561 y : minY + (this.unitWidth + this.gutter) * 2
35569 * remove a Masonry Brick
35570 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35572 removeBrick : function(brick_id)
35578 for (var i = 0; i<this.bricks.length; i++) {
35579 if (this.bricks[i].id == brick_id) {
35580 this.bricks.splice(i,1);
35581 this.el.dom.removeChild(Roo.get(brick_id).dom);
35588 * adds a Masonry Brick
35589 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35591 addBrick : function(cfg)
35593 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35594 //this.register(cn);
35595 cn.parentId = this.id;
35596 cn.render(this.el);
35601 * register a Masonry Brick
35602 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35605 register : function(brick)
35607 this.bricks.push(brick);
35608 brick.masonryId = this.id;
35612 * clear all the Masonry Brick
35614 clearAll : function()
35617 //this.getChildContainer().dom.innerHTML = "";
35618 this.el.dom.innerHTML = '';
35621 getSelected : function()
35623 if (!this.selectedBrick) {
35627 return this.selectedBrick;
35631 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35635 * register a Masonry Layout
35636 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35639 register : function(layout)
35641 this.groups[layout.id] = layout;
35644 * fetch a Masonry Layout based on the masonry layout ID
35645 * @param {string} the masonry layout to add
35646 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35649 get: function(layout_id) {
35650 if (typeof(this.groups[layout_id]) == 'undefined') {
35653 return this.groups[layout_id] ;
35665 * http://masonry.desandro.com
35667 * The idea is to render all the bricks based on vertical width...
35669 * The original code extends 'outlayer' - we might need to use that....
35675 * @class Roo.bootstrap.LayoutMasonryAuto
35676 * @extends Roo.bootstrap.Component
35677 * Bootstrap Layout Masonry class
35680 * Create a new Element
35681 * @param {Object} config The config object
35684 Roo.bootstrap.LayoutMasonryAuto = function(config){
35685 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35688 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35691 * @cfg {Boolean} isFitWidth - resize the width..
35693 isFitWidth : false, // options..
35695 * @cfg {Boolean} isOriginLeft = left align?
35697 isOriginLeft : true,
35699 * @cfg {Boolean} isOriginTop = top align?
35701 isOriginTop : false,
35703 * @cfg {Boolean} isLayoutInstant = no animation?
35705 isLayoutInstant : false, // needed?
35707 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35709 isResizingContainer : true,
35711 * @cfg {Number} columnWidth width of the columns
35717 * @cfg {Number} maxCols maximum number of columns
35722 * @cfg {Number} padHeight padding below box..
35728 * @cfg {Boolean} isAutoInitial defalut true
35731 isAutoInitial : true,
35737 initialColumnWidth : 0,
35738 currentSize : null,
35740 colYs : null, // array.
35747 bricks: null, //CompositeElement
35748 cols : 0, // array?
35749 // element : null, // wrapped now this.el
35750 _isLayoutInited : null,
35753 getAutoCreate : function(){
35757 cls: 'blog-masonary-wrapper ' + this.cls,
35759 cls : 'mas-boxes masonary'
35766 getChildContainer: function( )
35768 if (this.boxesEl) {
35769 return this.boxesEl;
35772 this.boxesEl = this.el.select('.mas-boxes').first();
35774 return this.boxesEl;
35778 initEvents : function()
35782 if(this.isAutoInitial){
35783 Roo.log('hook children rendered');
35784 this.on('childrenrendered', function() {
35785 Roo.log('children rendered');
35792 initial : function()
35794 this.reloadItems();
35796 this.currentSize = this.el.getBox(true);
35798 /// was window resize... - let's see if this works..
35799 Roo.EventManager.onWindowResize(this.resize, this);
35801 if(!this.isAutoInitial){
35806 this.layout.defer(500,this);
35809 reloadItems: function()
35811 this.bricks = this.el.select('.masonry-brick', true);
35813 this.bricks.each(function(b) {
35814 //Roo.log(b.getSize());
35815 if (!b.attr('originalwidth')) {
35816 b.attr('originalwidth', b.getSize().width);
35821 Roo.log(this.bricks.elements.length);
35824 resize : function()
35827 var cs = this.el.getBox(true);
35829 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35830 Roo.log("no change in with or X");
35833 this.currentSize = cs;
35837 layout : function()
35840 this._resetLayout();
35841 //this._manageStamps();
35843 // don't animate first layout
35844 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35845 this.layoutItems( isInstant );
35847 // flag for initalized
35848 this._isLayoutInited = true;
35851 layoutItems : function( isInstant )
35853 //var items = this._getItemsForLayout( this.items );
35854 // original code supports filtering layout items.. we just ignore it..
35856 this._layoutItems( this.bricks , isInstant );
35858 this._postLayout();
35860 _layoutItems : function ( items , isInstant)
35862 //this.fireEvent( 'layout', this, items );
35865 if ( !items || !items.elements.length ) {
35866 // no items, emit event with empty array
35871 items.each(function(item) {
35872 Roo.log("layout item");
35874 // get x/y object from method
35875 var position = this._getItemLayoutPosition( item );
35877 position.item = item;
35878 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35879 queue.push( position );
35882 this._processLayoutQueue( queue );
35884 /** Sets position of item in DOM
35885 * @param {Element} item
35886 * @param {Number} x - horizontal position
35887 * @param {Number} y - vertical position
35888 * @param {Boolean} isInstant - disables transitions
35890 _processLayoutQueue : function( queue )
35892 for ( var i=0, len = queue.length; i < len; i++ ) {
35893 var obj = queue[i];
35894 obj.item.position('absolute');
35895 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35901 * Any logic you want to do after each layout,
35902 * i.e. size the container
35904 _postLayout : function()
35906 this.resizeContainer();
35909 resizeContainer : function()
35911 if ( !this.isResizingContainer ) {
35914 var size = this._getContainerSize();
35916 this.el.setSize(size.width,size.height);
35917 this.boxesEl.setSize(size.width,size.height);
35923 _resetLayout : function()
35925 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35926 this.colWidth = this.el.getWidth();
35927 //this.gutter = this.el.getWidth();
35929 this.measureColumns();
35935 this.colYs.push( 0 );
35941 measureColumns : function()
35943 this.getContainerWidth();
35944 // if columnWidth is 0, default to outerWidth of first item
35945 if ( !this.columnWidth ) {
35946 var firstItem = this.bricks.first();
35947 Roo.log(firstItem);
35948 this.columnWidth = this.containerWidth;
35949 if (firstItem && firstItem.attr('originalwidth') ) {
35950 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35952 // columnWidth fall back to item of first element
35953 Roo.log("set column width?");
35954 this.initialColumnWidth = this.columnWidth ;
35956 // if first elem has no width, default to size of container
35961 if (this.initialColumnWidth) {
35962 this.columnWidth = this.initialColumnWidth;
35967 // column width is fixed at the top - however if container width get's smaller we should
35970 // this bit calcs how man columns..
35972 var columnWidth = this.columnWidth += this.gutter;
35974 // calculate columns
35975 var containerWidth = this.containerWidth + this.gutter;
35977 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35978 // fix rounding errors, typically with gutters
35979 var excess = columnWidth - containerWidth % columnWidth;
35982 // if overshoot is less than a pixel, round up, otherwise floor it
35983 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35984 cols = Math[ mathMethod ]( cols );
35985 this.cols = Math.max( cols, 1 );
35986 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35988 // padding positioning..
35989 var totalColWidth = this.cols * this.columnWidth;
35990 var padavail = this.containerWidth - totalColWidth;
35991 // so for 2 columns - we need 3 'pads'
35993 var padNeeded = (1+this.cols) * this.padWidth;
35995 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35997 this.columnWidth += padExtra
35998 //this.padWidth = Math.floor(padavail / ( this.cols));
36000 // adjust colum width so that padding is fixed??
36002 // we have 3 columns ... total = width * 3
36003 // we have X left over... that should be used by
36005 //if (this.expandC) {
36013 getContainerWidth : function()
36015 /* // container is parent if fit width
36016 var container = this.isFitWidth ? this.element.parentNode : this.element;
36017 // check that this.size and size are there
36018 // IE8 triggers resize on body size change, so they might not be
36020 var size = getSize( container ); //FIXME
36021 this.containerWidth = size && size.innerWidth; //FIXME
36024 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36028 _getItemLayoutPosition : function( item ) // what is item?
36030 // we resize the item to our columnWidth..
36032 item.setWidth(this.columnWidth);
36033 item.autoBoxAdjust = false;
36035 var sz = item.getSize();
36037 // how many columns does this brick span
36038 var remainder = this.containerWidth % this.columnWidth;
36040 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36041 // round if off by 1 pixel, otherwise use ceil
36042 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36043 colSpan = Math.min( colSpan, this.cols );
36045 // normally this should be '1' as we dont' currently allow multi width columns..
36047 var colGroup = this._getColGroup( colSpan );
36048 // get the minimum Y value from the columns
36049 var minimumY = Math.min.apply( Math, colGroup );
36050 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36052 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36054 // position the brick
36056 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36057 y: this.currentSize.y + minimumY + this.padHeight
36061 // apply setHeight to necessary columns
36062 var setHeight = minimumY + sz.height + this.padHeight;
36063 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36065 var setSpan = this.cols + 1 - colGroup.length;
36066 for ( var i = 0; i < setSpan; i++ ) {
36067 this.colYs[ shortColIndex + i ] = setHeight ;
36074 * @param {Number} colSpan - number of columns the element spans
36075 * @returns {Array} colGroup
36077 _getColGroup : function( colSpan )
36079 if ( colSpan < 2 ) {
36080 // if brick spans only one column, use all the column Ys
36085 // how many different places could this brick fit horizontally
36086 var groupCount = this.cols + 1 - colSpan;
36087 // for each group potential horizontal position
36088 for ( var i = 0; i < groupCount; i++ ) {
36089 // make an array of colY values for that one group
36090 var groupColYs = this.colYs.slice( i, i + colSpan );
36091 // and get the max value of the array
36092 colGroup[i] = Math.max.apply( Math, groupColYs );
36097 _manageStamp : function( stamp )
36099 var stampSize = stamp.getSize();
36100 var offset = stamp.getBox();
36101 // get the columns that this stamp affects
36102 var firstX = this.isOriginLeft ? offset.x : offset.right;
36103 var lastX = firstX + stampSize.width;
36104 var firstCol = Math.floor( firstX / this.columnWidth );
36105 firstCol = Math.max( 0, firstCol );
36107 var lastCol = Math.floor( lastX / this.columnWidth );
36108 // lastCol should not go over if multiple of columnWidth #425
36109 lastCol -= lastX % this.columnWidth ? 0 : 1;
36110 lastCol = Math.min( this.cols - 1, lastCol );
36112 // set colYs to bottom of the stamp
36113 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36116 for ( var i = firstCol; i <= lastCol; i++ ) {
36117 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36122 _getContainerSize : function()
36124 this.maxY = Math.max.apply( Math, this.colYs );
36129 if ( this.isFitWidth ) {
36130 size.width = this._getContainerFitWidth();
36136 _getContainerFitWidth : function()
36138 var unusedCols = 0;
36139 // count unused columns
36142 if ( this.colYs[i] !== 0 ) {
36147 // fit container to columns that have been used
36148 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36151 needsResizeLayout : function()
36153 var previousWidth = this.containerWidth;
36154 this.getContainerWidth();
36155 return previousWidth !== this.containerWidth;
36170 * @class Roo.bootstrap.MasonryBrick
36171 * @extends Roo.bootstrap.Component
36172 * Bootstrap MasonryBrick class
36175 * Create a new MasonryBrick
36176 * @param {Object} config The config object
36179 Roo.bootstrap.MasonryBrick = function(config){
36181 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36183 Roo.bootstrap.MasonryBrick.register(this);
36189 * When a MasonryBrick is clcik
36190 * @param {Roo.bootstrap.MasonryBrick} this
36191 * @param {Roo.EventObject} e
36197 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36200 * @cfg {String} title
36204 * @cfg {String} html
36208 * @cfg {String} bgimage
36212 * @cfg {String} videourl
36216 * @cfg {String} cls
36220 * @cfg {String} href
36224 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36229 * @cfg {String} placetitle (center|bottom)
36234 * @cfg {Boolean} isFitContainer defalut true
36236 isFitContainer : true,
36239 * @cfg {Boolean} preventDefault defalut false
36241 preventDefault : false,
36244 * @cfg {Boolean} inverse defalut false
36246 maskInverse : false,
36248 getAutoCreate : function()
36250 if(!this.isFitContainer){
36251 return this.getSplitAutoCreate();
36254 var cls = 'masonry-brick masonry-brick-full';
36256 if(this.href.length){
36257 cls += ' masonry-brick-link';
36260 if(this.bgimage.length){
36261 cls += ' masonry-brick-image';
36264 if(this.maskInverse){
36265 cls += ' mask-inverse';
36268 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36269 cls += ' enable-mask';
36273 cls += ' masonry-' + this.size + '-brick';
36276 if(this.placetitle.length){
36278 switch (this.placetitle) {
36280 cls += ' masonry-center-title';
36283 cls += ' masonry-bottom-title';
36290 if(!this.html.length && !this.bgimage.length){
36291 cls += ' masonry-center-title';
36294 if(!this.html.length && this.bgimage.length){
36295 cls += ' masonry-bottom-title';
36300 cls += ' ' + this.cls;
36304 tag: (this.href.length) ? 'a' : 'div',
36309 cls: 'masonry-brick-mask'
36313 cls: 'masonry-brick-paragraph',
36319 if(this.href.length){
36320 cfg.href = this.href;
36323 var cn = cfg.cn[1].cn;
36325 if(this.title.length){
36328 cls: 'masonry-brick-title',
36333 if(this.html.length){
36336 cls: 'masonry-brick-text',
36341 if (!this.title.length && !this.html.length) {
36342 cfg.cn[1].cls += ' hide';
36345 if(this.bgimage.length){
36348 cls: 'masonry-brick-image-view',
36353 if(this.videourl.length){
36354 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36355 // youtube support only?
36358 cls: 'masonry-brick-image-view',
36361 allowfullscreen : true
36369 getSplitAutoCreate : function()
36371 var cls = 'masonry-brick masonry-brick-split';
36373 if(this.href.length){
36374 cls += ' masonry-brick-link';
36377 if(this.bgimage.length){
36378 cls += ' masonry-brick-image';
36382 cls += ' masonry-' + this.size + '-brick';
36385 switch (this.placetitle) {
36387 cls += ' masonry-center-title';
36390 cls += ' masonry-bottom-title';
36393 if(!this.bgimage.length){
36394 cls += ' masonry-center-title';
36397 if(this.bgimage.length){
36398 cls += ' masonry-bottom-title';
36404 cls += ' ' + this.cls;
36408 tag: (this.href.length) ? 'a' : 'div',
36413 cls: 'masonry-brick-split-head',
36417 cls: 'masonry-brick-paragraph',
36424 cls: 'masonry-brick-split-body',
36430 if(this.href.length){
36431 cfg.href = this.href;
36434 if(this.title.length){
36435 cfg.cn[0].cn[0].cn.push({
36437 cls: 'masonry-brick-title',
36442 if(this.html.length){
36443 cfg.cn[1].cn.push({
36445 cls: 'masonry-brick-text',
36450 if(this.bgimage.length){
36451 cfg.cn[0].cn.push({
36453 cls: 'masonry-brick-image-view',
36458 if(this.videourl.length){
36459 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36460 // youtube support only?
36461 cfg.cn[0].cn.cn.push({
36463 cls: 'masonry-brick-image-view',
36466 allowfullscreen : true
36473 initEvents: function()
36475 switch (this.size) {
36508 this.el.on('touchstart', this.onTouchStart, this);
36509 this.el.on('touchmove', this.onTouchMove, this);
36510 this.el.on('touchend', this.onTouchEnd, this);
36511 this.el.on('contextmenu', this.onContextMenu, this);
36513 this.el.on('mouseenter' ,this.enter, this);
36514 this.el.on('mouseleave', this.leave, this);
36515 this.el.on('click', this.onClick, this);
36518 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36519 this.parent().bricks.push(this);
36524 onClick: function(e, el)
36526 var time = this.endTimer - this.startTimer;
36527 // Roo.log(e.preventDefault());
36530 e.preventDefault();
36535 if(!this.preventDefault){
36539 e.preventDefault();
36541 if (this.activeClass != '') {
36542 this.selectBrick();
36545 this.fireEvent('click', this, e);
36548 enter: function(e, el)
36550 e.preventDefault();
36552 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36556 if(this.bgimage.length && this.html.length){
36557 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36561 leave: function(e, el)
36563 e.preventDefault();
36565 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36569 if(this.bgimage.length && this.html.length){
36570 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36574 onTouchStart: function(e, el)
36576 // e.preventDefault();
36578 this.touchmoved = false;
36580 if(!this.isFitContainer){
36584 if(!this.bgimage.length || !this.html.length){
36588 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36590 this.timer = new Date().getTime();
36594 onTouchMove: function(e, el)
36596 this.touchmoved = true;
36599 onContextMenu : function(e,el)
36601 e.preventDefault();
36602 e.stopPropagation();
36606 onTouchEnd: function(e, el)
36608 // e.preventDefault();
36610 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36617 if(!this.bgimage.length || !this.html.length){
36619 if(this.href.length){
36620 window.location.href = this.href;
36626 if(!this.isFitContainer){
36630 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36632 window.location.href = this.href;
36635 //selection on single brick only
36636 selectBrick : function() {
36638 if (!this.parentId) {
36642 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36643 var index = m.selectedBrick.indexOf(this.id);
36646 m.selectedBrick.splice(index,1);
36647 this.el.removeClass(this.activeClass);
36651 for(var i = 0; i < m.selectedBrick.length; i++) {
36652 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36653 b.el.removeClass(b.activeClass);
36656 m.selectedBrick = [];
36658 m.selectedBrick.push(this.id);
36659 this.el.addClass(this.activeClass);
36663 isSelected : function(){
36664 return this.el.hasClass(this.activeClass);
36669 Roo.apply(Roo.bootstrap.MasonryBrick, {
36672 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36674 * register a Masonry Brick
36675 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36678 register : function(brick)
36680 //this.groups[brick.id] = brick;
36681 this.groups.add(brick.id, brick);
36684 * fetch a masonry brick based on the masonry brick ID
36685 * @param {string} the masonry brick to add
36686 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36689 get: function(brick_id)
36691 // if (typeof(this.groups[brick_id]) == 'undefined') {
36694 // return this.groups[brick_id] ;
36696 if(this.groups.key(brick_id)) {
36697 return this.groups.key(brick_id);
36715 * @class Roo.bootstrap.Brick
36716 * @extends Roo.bootstrap.Component
36717 * Bootstrap Brick class
36720 * Create a new Brick
36721 * @param {Object} config The config object
36724 Roo.bootstrap.Brick = function(config){
36725 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36731 * When a Brick is click
36732 * @param {Roo.bootstrap.Brick} this
36733 * @param {Roo.EventObject} e
36739 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36742 * @cfg {String} title
36746 * @cfg {String} html
36750 * @cfg {String} bgimage
36754 * @cfg {String} cls
36758 * @cfg {String} href
36762 * @cfg {String} video
36766 * @cfg {Boolean} square
36770 getAutoCreate : function()
36772 var cls = 'roo-brick';
36774 if(this.href.length){
36775 cls += ' roo-brick-link';
36778 if(this.bgimage.length){
36779 cls += ' roo-brick-image';
36782 if(!this.html.length && !this.bgimage.length){
36783 cls += ' roo-brick-center-title';
36786 if(!this.html.length && this.bgimage.length){
36787 cls += ' roo-brick-bottom-title';
36791 cls += ' ' + this.cls;
36795 tag: (this.href.length) ? 'a' : 'div',
36800 cls: 'roo-brick-paragraph',
36806 if(this.href.length){
36807 cfg.href = this.href;
36810 var cn = cfg.cn[0].cn;
36812 if(this.title.length){
36815 cls: 'roo-brick-title',
36820 if(this.html.length){
36823 cls: 'roo-brick-text',
36830 if(this.bgimage.length){
36833 cls: 'roo-brick-image-view',
36841 initEvents: function()
36843 if(this.title.length || this.html.length){
36844 this.el.on('mouseenter' ,this.enter, this);
36845 this.el.on('mouseleave', this.leave, this);
36848 Roo.EventManager.onWindowResize(this.resize, this);
36850 if(this.bgimage.length){
36851 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36852 this.imageEl.on('load', this.onImageLoad, this);
36859 onImageLoad : function()
36864 resize : function()
36866 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36868 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36870 if(this.bgimage.length){
36871 var image = this.el.select('.roo-brick-image-view', true).first();
36873 image.setWidth(paragraph.getWidth());
36876 image.setHeight(paragraph.getWidth());
36879 this.el.setHeight(image.getHeight());
36880 paragraph.setHeight(image.getHeight());
36886 enter: function(e, el)
36888 e.preventDefault();
36890 if(this.bgimage.length){
36891 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36892 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36896 leave: function(e, el)
36898 e.preventDefault();
36900 if(this.bgimage.length){
36901 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36902 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36917 * @class Roo.bootstrap.NumberField
36918 * @extends Roo.bootstrap.Input
36919 * Bootstrap NumberField class
36925 * Create a new NumberField
36926 * @param {Object} config The config object
36929 Roo.bootstrap.NumberField = function(config){
36930 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36933 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36936 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36938 allowDecimals : true,
36940 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36942 decimalSeparator : ".",
36944 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36946 decimalPrecision : 2,
36948 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36950 allowNegative : true,
36953 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36957 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36959 minValue : Number.NEGATIVE_INFINITY,
36961 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36963 maxValue : Number.MAX_VALUE,
36965 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36967 minText : "The minimum value for this field is {0}",
36969 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36971 maxText : "The maximum value for this field is {0}",
36973 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36974 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36976 nanText : "{0} is not a valid number",
36978 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36980 thousandsDelimiter : false,
36982 * @cfg {String} valueAlign alignment of value
36984 valueAlign : "left",
36986 getAutoCreate : function()
36988 var hiddenInput = {
36992 cls: 'hidden-number-input'
36996 hiddenInput.name = this.name;
37001 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37003 this.name = hiddenInput.name;
37005 if(cfg.cn.length > 0) {
37006 cfg.cn.push(hiddenInput);
37013 initEvents : function()
37015 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37017 var allowed = "0123456789";
37019 if(this.allowDecimals){
37020 allowed += this.decimalSeparator;
37023 if(this.allowNegative){
37027 if(this.thousandsDelimiter) {
37031 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37033 var keyPress = function(e){
37035 var k = e.getKey();
37037 var c = e.getCharCode();
37040 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37041 allowed.indexOf(String.fromCharCode(c)) === -1
37047 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37051 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37056 this.el.on("keypress", keyPress, this);
37059 validateValue : function(value)
37062 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37066 var num = this.parseValue(value);
37069 this.markInvalid(String.format(this.nanText, value));
37073 if(num < this.minValue){
37074 this.markInvalid(String.format(this.minText, this.minValue));
37078 if(num > this.maxValue){
37079 this.markInvalid(String.format(this.maxText, this.maxValue));
37086 getValue : function()
37088 var v = this.hiddenEl().getValue();
37090 return this.fixPrecision(this.parseValue(v));
37093 parseValue : function(value)
37095 if(this.thousandsDelimiter) {
37097 r = new RegExp(",", "g");
37098 value = value.replace(r, "");
37101 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37102 return isNaN(value) ? '' : value;
37105 fixPrecision : function(value)
37107 if(this.thousandsDelimiter) {
37109 r = new RegExp(",", "g");
37110 value = value.replace(r, "");
37113 var nan = isNaN(value);
37115 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37116 return nan ? '' : value;
37118 return parseFloat(value).toFixed(this.decimalPrecision);
37121 setValue : function(v)
37123 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37129 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37131 this.inputEl().dom.value = (v == '') ? '' :
37132 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37134 if(!this.allowZero && v === '0') {
37135 this.hiddenEl().dom.value = '';
37136 this.inputEl().dom.value = '';
37143 decimalPrecisionFcn : function(v)
37145 return Math.floor(v);
37148 beforeBlur : function()
37150 var v = this.parseValue(this.getRawValue());
37152 if(v || v === 0 || v === ''){
37157 hiddenEl : function()
37159 return this.el.select('input.hidden-number-input',true).first();
37171 * @class Roo.bootstrap.DocumentSlider
37172 * @extends Roo.bootstrap.Component
37173 * Bootstrap DocumentSlider class
37176 * Create a new DocumentViewer
37177 * @param {Object} config The config object
37180 Roo.bootstrap.DocumentSlider = function(config){
37181 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37188 * Fire after initEvent
37189 * @param {Roo.bootstrap.DocumentSlider} this
37194 * Fire after update
37195 * @param {Roo.bootstrap.DocumentSlider} this
37201 * @param {Roo.bootstrap.DocumentSlider} this
37207 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37213 getAutoCreate : function()
37217 cls : 'roo-document-slider',
37221 cls : 'roo-document-slider-header',
37225 cls : 'roo-document-slider-header-title'
37231 cls : 'roo-document-slider-body',
37235 cls : 'roo-document-slider-prev',
37239 cls : 'fa fa-chevron-left'
37245 cls : 'roo-document-slider-thumb',
37249 cls : 'roo-document-slider-image'
37255 cls : 'roo-document-slider-next',
37259 cls : 'fa fa-chevron-right'
37271 initEvents : function()
37273 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37274 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37276 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37277 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37279 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37280 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37282 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37283 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37285 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37286 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37288 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37289 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37291 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37292 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37294 this.thumbEl.on('click', this.onClick, this);
37296 this.prevIndicator.on('click', this.prev, this);
37298 this.nextIndicator.on('click', this.next, this);
37302 initial : function()
37304 if(this.files.length){
37305 this.indicator = 1;
37309 this.fireEvent('initial', this);
37312 update : function()
37314 this.imageEl.attr('src', this.files[this.indicator - 1]);
37316 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37318 this.prevIndicator.show();
37320 if(this.indicator == 1){
37321 this.prevIndicator.hide();
37324 this.nextIndicator.show();
37326 if(this.indicator == this.files.length){
37327 this.nextIndicator.hide();
37330 this.thumbEl.scrollTo('top');
37332 this.fireEvent('update', this);
37335 onClick : function(e)
37337 e.preventDefault();
37339 this.fireEvent('click', this);
37344 e.preventDefault();
37346 this.indicator = Math.max(1, this.indicator - 1);
37353 e.preventDefault();
37355 this.indicator = Math.min(this.files.length, this.indicator + 1);
37369 * @class Roo.bootstrap.RadioSet
37370 * @extends Roo.bootstrap.Input
37371 * Bootstrap RadioSet class
37372 * @cfg {String} indicatorpos (left|right) default left
37373 * @cfg {Boolean} inline (true|false) inline the element (default true)
37374 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37376 * Create a new RadioSet
37377 * @param {Object} config The config object
37380 Roo.bootstrap.RadioSet = function(config){
37382 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37386 Roo.bootstrap.RadioSet.register(this);
37391 * Fires when the element is checked or unchecked.
37392 * @param {Roo.bootstrap.RadioSet} this This radio
37393 * @param {Roo.bootstrap.Radio} item The checked item
37398 * Fires when the element is click.
37399 * @param {Roo.bootstrap.RadioSet} this This radio set
37400 * @param {Roo.bootstrap.Radio} item The checked item
37401 * @param {Roo.EventObject} e The event object
37408 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37416 indicatorpos : 'left',
37418 getAutoCreate : function()
37422 cls : 'roo-radio-set-label',
37426 html : this.fieldLabel
37430 if (Roo.bootstrap.version == 3) {
37433 if(this.indicatorpos == 'left'){
37436 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37437 tooltip : 'This field is required'
37442 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37443 tooltip : 'This field is required'
37449 cls : 'roo-radio-set-items'
37452 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37454 if (align === 'left' && this.fieldLabel.length) {
37457 cls : "roo-radio-set-right",
37463 if(this.labelWidth > 12){
37464 label.style = "width: " + this.labelWidth + 'px';
37467 if(this.labelWidth < 13 && this.labelmd == 0){
37468 this.labelmd = this.labelWidth;
37471 if(this.labellg > 0){
37472 label.cls += ' col-lg-' + this.labellg;
37473 items.cls += ' col-lg-' + (12 - this.labellg);
37476 if(this.labelmd > 0){
37477 label.cls += ' col-md-' + this.labelmd;
37478 items.cls += ' col-md-' + (12 - this.labelmd);
37481 if(this.labelsm > 0){
37482 label.cls += ' col-sm-' + this.labelsm;
37483 items.cls += ' col-sm-' + (12 - this.labelsm);
37486 if(this.labelxs > 0){
37487 label.cls += ' col-xs-' + this.labelxs;
37488 items.cls += ' col-xs-' + (12 - this.labelxs);
37494 cls : 'roo-radio-set',
37498 cls : 'roo-radio-set-input',
37501 value : this.value ? this.value : ''
37508 if(this.weight.length){
37509 cfg.cls += ' roo-radio-' + this.weight;
37513 cfg.cls += ' roo-radio-set-inline';
37517 ['xs','sm','md','lg'].map(function(size){
37518 if (settings[size]) {
37519 cfg.cls += ' col-' + size + '-' + settings[size];
37527 initEvents : function()
37529 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37530 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37532 if(!this.fieldLabel.length){
37533 this.labelEl.hide();
37536 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37537 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37539 this.indicator = this.indicatorEl();
37541 if(this.indicator){
37542 this.indicator.addClass('invisible');
37545 this.originalValue = this.getValue();
37549 inputEl: function ()
37551 return this.el.select('.roo-radio-set-input', true).first();
37554 getChildContainer : function()
37556 return this.itemsEl;
37559 register : function(item)
37561 this.radioes.push(item);
37565 validate : function()
37567 if(this.getVisibilityEl().hasClass('hidden')){
37573 Roo.each(this.radioes, function(i){
37582 if(this.allowBlank) {
37586 if(this.disabled || valid){
37591 this.markInvalid();
37596 markValid : function()
37598 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37599 this.indicatorEl().removeClass('visible');
37600 this.indicatorEl().addClass('invisible');
37604 if (Roo.bootstrap.version == 3) {
37605 this.el.removeClass([this.invalidClass, this.validClass]);
37606 this.el.addClass(this.validClass);
37608 this.el.removeClass(['is-invalid','is-valid']);
37609 this.el.addClass(['is-valid']);
37611 this.fireEvent('valid', this);
37614 markInvalid : function(msg)
37616 if(this.allowBlank || this.disabled){
37620 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37621 this.indicatorEl().removeClass('invisible');
37622 this.indicatorEl().addClass('visible');
37624 if (Roo.bootstrap.version == 3) {
37625 this.el.removeClass([this.invalidClass, this.validClass]);
37626 this.el.addClass(this.invalidClass);
37628 this.el.removeClass(['is-invalid','is-valid']);
37629 this.el.addClass(['is-invalid']);
37632 this.fireEvent('invalid', this, msg);
37636 setValue : function(v, suppressEvent)
37638 if(this.value === v){
37645 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37648 Roo.each(this.radioes, function(i){
37650 i.el.removeClass('checked');
37653 Roo.each(this.radioes, function(i){
37655 if(i.value === v || i.value.toString() === v.toString()){
37657 i.el.addClass('checked');
37659 if(suppressEvent !== true){
37660 this.fireEvent('check', this, i);
37671 clearInvalid : function(){
37673 if(!this.el || this.preventMark){
37677 this.el.removeClass([this.invalidClass]);
37679 this.fireEvent('valid', this);
37684 Roo.apply(Roo.bootstrap.RadioSet, {
37688 register : function(set)
37690 this.groups[set.name] = set;
37693 get: function(name)
37695 if (typeof(this.groups[name]) == 'undefined') {
37699 return this.groups[name] ;
37705 * Ext JS Library 1.1.1
37706 * Copyright(c) 2006-2007, Ext JS, LLC.
37708 * Originally Released Under LGPL - original licence link has changed is not relivant.
37711 * <script type="text/javascript">
37716 * @class Roo.bootstrap.SplitBar
37717 * @extends Roo.util.Observable
37718 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37722 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37723 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37724 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37725 split.minSize = 100;
37726 split.maxSize = 600;
37727 split.animate = true;
37728 split.on('moved', splitterMoved);
37731 * Create a new SplitBar
37732 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37733 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37734 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37735 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37736 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37737 position of the SplitBar).
37739 Roo.bootstrap.SplitBar = function(cfg){
37744 // dragElement : elm
37745 // resizingElement: el,
37747 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37748 // placement : Roo.bootstrap.SplitBar.LEFT ,
37749 // existingProxy ???
37752 this.el = Roo.get(cfg.dragElement, true);
37753 this.el.dom.unselectable = "on";
37755 this.resizingEl = Roo.get(cfg.resizingElement, true);
37759 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37760 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37763 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37766 * The minimum size of the resizing element. (Defaults to 0)
37772 * The maximum size of the resizing element. (Defaults to 2000)
37775 this.maxSize = 2000;
37778 * Whether to animate the transition to the new size
37781 this.animate = false;
37784 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37787 this.useShim = false;
37792 if(!cfg.existingProxy){
37794 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37796 this.proxy = Roo.get(cfg.existingProxy).dom;
37799 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37802 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37805 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37808 this.dragSpecs = {};
37811 * @private The adapter to use to positon and resize elements
37813 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37814 this.adapter.init(this);
37816 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37818 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37819 this.el.addClass("roo-splitbar-h");
37822 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37823 this.el.addClass("roo-splitbar-v");
37829 * Fires when the splitter is moved (alias for {@link #event-moved})
37830 * @param {Roo.bootstrap.SplitBar} this
37831 * @param {Number} newSize the new width or height
37836 * Fires when the splitter is moved
37837 * @param {Roo.bootstrap.SplitBar} this
37838 * @param {Number} newSize the new width or height
37842 * @event beforeresize
37843 * Fires before the splitter is dragged
37844 * @param {Roo.bootstrap.SplitBar} this
37846 "beforeresize" : true,
37848 "beforeapply" : true
37851 Roo.util.Observable.call(this);
37854 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37855 onStartProxyDrag : function(x, y){
37856 this.fireEvent("beforeresize", this);
37858 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37860 o.enableDisplayMode("block");
37861 // all splitbars share the same overlay
37862 Roo.bootstrap.SplitBar.prototype.overlay = o;
37864 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37865 this.overlay.show();
37866 Roo.get(this.proxy).setDisplayed("block");
37867 var size = this.adapter.getElementSize(this);
37868 this.activeMinSize = this.getMinimumSize();;
37869 this.activeMaxSize = this.getMaximumSize();;
37870 var c1 = size - this.activeMinSize;
37871 var c2 = Math.max(this.activeMaxSize - size, 0);
37872 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37873 this.dd.resetConstraints();
37874 this.dd.setXConstraint(
37875 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37876 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37878 this.dd.setYConstraint(0, 0);
37880 this.dd.resetConstraints();
37881 this.dd.setXConstraint(0, 0);
37882 this.dd.setYConstraint(
37883 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37884 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37887 this.dragSpecs.startSize = size;
37888 this.dragSpecs.startPoint = [x, y];
37889 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37893 * @private Called after the drag operation by the DDProxy
37895 onEndProxyDrag : function(e){
37896 Roo.get(this.proxy).setDisplayed(false);
37897 var endPoint = Roo.lib.Event.getXY(e);
37899 this.overlay.hide();
37902 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37903 newSize = this.dragSpecs.startSize +
37904 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37905 endPoint[0] - this.dragSpecs.startPoint[0] :
37906 this.dragSpecs.startPoint[0] - endPoint[0]
37909 newSize = this.dragSpecs.startSize +
37910 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37911 endPoint[1] - this.dragSpecs.startPoint[1] :
37912 this.dragSpecs.startPoint[1] - endPoint[1]
37915 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37916 if(newSize != this.dragSpecs.startSize){
37917 if(this.fireEvent('beforeapply', this, newSize) !== false){
37918 this.adapter.setElementSize(this, newSize);
37919 this.fireEvent("moved", this, newSize);
37920 this.fireEvent("resize", this, newSize);
37926 * Get the adapter this SplitBar uses
37927 * @return The adapter object
37929 getAdapter : function(){
37930 return this.adapter;
37934 * Set the adapter this SplitBar uses
37935 * @param {Object} adapter A SplitBar adapter object
37937 setAdapter : function(adapter){
37938 this.adapter = adapter;
37939 this.adapter.init(this);
37943 * Gets the minimum size for the resizing element
37944 * @return {Number} The minimum size
37946 getMinimumSize : function(){
37947 return this.minSize;
37951 * Sets the minimum size for the resizing element
37952 * @param {Number} minSize The minimum size
37954 setMinimumSize : function(minSize){
37955 this.minSize = minSize;
37959 * Gets the maximum size for the resizing element
37960 * @return {Number} The maximum size
37962 getMaximumSize : function(){
37963 return this.maxSize;
37967 * Sets the maximum size for the resizing element
37968 * @param {Number} maxSize The maximum size
37970 setMaximumSize : function(maxSize){
37971 this.maxSize = maxSize;
37975 * Sets the initialize size for the resizing element
37976 * @param {Number} size The initial size
37978 setCurrentSize : function(size){
37979 var oldAnimate = this.animate;
37980 this.animate = false;
37981 this.adapter.setElementSize(this, size);
37982 this.animate = oldAnimate;
37986 * Destroy this splitbar.
37987 * @param {Boolean} removeEl True to remove the element
37989 destroy : function(removeEl){
37991 this.shim.remove();
37994 this.proxy.parentNode.removeChild(this.proxy);
38002 * @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.
38004 Roo.bootstrap.SplitBar.createProxy = function(dir){
38005 var proxy = new Roo.Element(document.createElement("div"));
38006 proxy.unselectable();
38007 var cls = 'roo-splitbar-proxy';
38008 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38009 document.body.appendChild(proxy.dom);
38014 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38015 * Default Adapter. It assumes the splitter and resizing element are not positioned
38016 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38018 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38021 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38022 // do nothing for now
38023 init : function(s){
38027 * Called before drag operations to get the current size of the resizing element.
38028 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38030 getElementSize : function(s){
38031 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38032 return s.resizingEl.getWidth();
38034 return s.resizingEl.getHeight();
38039 * Called after drag operations to set the size of the resizing element.
38040 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38041 * @param {Number} newSize The new size to set
38042 * @param {Function} onComplete A function to be invoked when resizing is complete
38044 setElementSize : function(s, newSize, onComplete){
38045 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38047 s.resizingEl.setWidth(newSize);
38049 onComplete(s, newSize);
38052 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38057 s.resizingEl.setHeight(newSize);
38059 onComplete(s, newSize);
38062 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38069 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38070 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38071 * Adapter that moves the splitter element to align with the resized sizing element.
38072 * Used with an absolute positioned SplitBar.
38073 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38074 * document.body, make sure you assign an id to the body element.
38076 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38077 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38078 this.container = Roo.get(container);
38081 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38082 init : function(s){
38083 this.basic.init(s);
38086 getElementSize : function(s){
38087 return this.basic.getElementSize(s);
38090 setElementSize : function(s, newSize, onComplete){
38091 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38094 moveSplitter : function(s){
38095 var yes = Roo.bootstrap.SplitBar;
38096 switch(s.placement){
38098 s.el.setX(s.resizingEl.getRight());
38101 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38104 s.el.setY(s.resizingEl.getBottom());
38107 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38114 * Orientation constant - Create a vertical SplitBar
38118 Roo.bootstrap.SplitBar.VERTICAL = 1;
38121 * Orientation constant - Create a horizontal SplitBar
38125 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38128 * Placement constant - The resizing element is to the left of the splitter element
38132 Roo.bootstrap.SplitBar.LEFT = 1;
38135 * Placement constant - The resizing element is to the right of the splitter element
38139 Roo.bootstrap.SplitBar.RIGHT = 2;
38142 * Placement constant - The resizing element is positioned above the splitter element
38146 Roo.bootstrap.SplitBar.TOP = 3;
38149 * Placement constant - The resizing element is positioned under splitter element
38153 Roo.bootstrap.SplitBar.BOTTOM = 4;
38154 Roo.namespace("Roo.bootstrap.layout");/*
38156 * Ext JS Library 1.1.1
38157 * Copyright(c) 2006-2007, Ext JS, LLC.
38159 * Originally Released Under LGPL - original licence link has changed is not relivant.
38162 * <script type="text/javascript">
38166 * @class Roo.bootstrap.layout.Manager
38167 * @extends Roo.bootstrap.Component
38168 * Base class for layout managers.
38170 Roo.bootstrap.layout.Manager = function(config)
38172 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38178 /** false to disable window resize monitoring @type Boolean */
38179 this.monitorWindowResize = true;
38184 * Fires when a layout is performed.
38185 * @param {Roo.LayoutManager} this
38189 * @event regionresized
38190 * Fires when the user resizes a region.
38191 * @param {Roo.LayoutRegion} region The resized region
38192 * @param {Number} newSize The new size (width for east/west, height for north/south)
38194 "regionresized" : true,
38196 * @event regioncollapsed
38197 * Fires when a region is collapsed.
38198 * @param {Roo.LayoutRegion} region The collapsed region
38200 "regioncollapsed" : true,
38202 * @event regionexpanded
38203 * Fires when a region is expanded.
38204 * @param {Roo.LayoutRegion} region The expanded region
38206 "regionexpanded" : true
38208 this.updating = false;
38211 this.el = Roo.get(config.el);
38217 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38222 monitorWindowResize : true,
38228 onRender : function(ct, position)
38231 this.el = Roo.get(ct);
38234 //this.fireEvent('render',this);
38238 initEvents: function()
38242 // ie scrollbar fix
38243 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38244 document.body.scroll = "no";
38245 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38246 this.el.position('relative');
38248 this.id = this.el.id;
38249 this.el.addClass("roo-layout-container");
38250 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38251 if(this.el.dom != document.body ) {
38252 this.el.on('resize', this.layout,this);
38253 this.el.on('show', this.layout,this);
38259 * Returns true if this layout is currently being updated
38260 * @return {Boolean}
38262 isUpdating : function(){
38263 return this.updating;
38267 * Suspend the LayoutManager from doing auto-layouts while
38268 * making multiple add or remove calls
38270 beginUpdate : function(){
38271 this.updating = true;
38275 * Restore auto-layouts and optionally disable the manager from performing a layout
38276 * @param {Boolean} noLayout true to disable a layout update
38278 endUpdate : function(noLayout){
38279 this.updating = false;
38285 layout: function(){
38289 onRegionResized : function(region, newSize){
38290 this.fireEvent("regionresized", region, newSize);
38294 onRegionCollapsed : function(region){
38295 this.fireEvent("regioncollapsed", region);
38298 onRegionExpanded : function(region){
38299 this.fireEvent("regionexpanded", region);
38303 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38304 * performs box-model adjustments.
38305 * @return {Object} The size as an object {width: (the width), height: (the height)}
38307 getViewSize : function()
38310 if(this.el.dom != document.body){
38311 size = this.el.getSize();
38313 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38315 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38316 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38321 * Returns the Element this layout is bound to.
38322 * @return {Roo.Element}
38324 getEl : function(){
38329 * Returns the specified region.
38330 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38331 * @return {Roo.LayoutRegion}
38333 getRegion : function(target){
38334 return this.regions[target.toLowerCase()];
38337 onWindowResize : function(){
38338 if(this.monitorWindowResize){
38345 * Ext JS Library 1.1.1
38346 * Copyright(c) 2006-2007, Ext JS, LLC.
38348 * Originally Released Under LGPL - original licence link has changed is not relivant.
38351 * <script type="text/javascript">
38354 * @class Roo.bootstrap.layout.Border
38355 * @extends Roo.bootstrap.layout.Manager
38356 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38357 * please see: examples/bootstrap/nested.html<br><br>
38359 <b>The container the layout is rendered into can be either the body element or any other element.
38360 If it is not the body element, the container needs to either be an absolute positioned element,
38361 or you will need to add "position:relative" to the css of the container. You will also need to specify
38362 the container size if it is not the body element.</b>
38365 * Create a new Border
38366 * @param {Object} config Configuration options
38368 Roo.bootstrap.layout.Border = function(config){
38369 config = config || {};
38370 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38374 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38375 if(config[region]){
38376 config[region].region = region;
38377 this.addRegion(config[region]);
38383 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38385 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38387 parent : false, // this might point to a 'nest' or a ???
38390 * Creates and adds a new region if it doesn't already exist.
38391 * @param {String} target The target region key (north, south, east, west or center).
38392 * @param {Object} config The regions config object
38393 * @return {BorderLayoutRegion} The new region
38395 addRegion : function(config)
38397 if(!this.regions[config.region]){
38398 var r = this.factory(config);
38399 this.bindRegion(r);
38401 return this.regions[config.region];
38405 bindRegion : function(r){
38406 this.regions[r.config.region] = r;
38408 r.on("visibilitychange", this.layout, this);
38409 r.on("paneladded", this.layout, this);
38410 r.on("panelremoved", this.layout, this);
38411 r.on("invalidated", this.layout, this);
38412 r.on("resized", this.onRegionResized, this);
38413 r.on("collapsed", this.onRegionCollapsed, this);
38414 r.on("expanded", this.onRegionExpanded, this);
38418 * Performs a layout update.
38420 layout : function()
38422 if(this.updating) {
38426 // render all the rebions if they have not been done alreayd?
38427 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38428 if(this.regions[region] && !this.regions[region].bodyEl){
38429 this.regions[region].onRender(this.el)
38433 var size = this.getViewSize();
38434 var w = size.width;
38435 var h = size.height;
38440 //var x = 0, y = 0;
38442 var rs = this.regions;
38443 var north = rs["north"];
38444 var south = rs["south"];
38445 var west = rs["west"];
38446 var east = rs["east"];
38447 var center = rs["center"];
38448 //if(this.hideOnLayout){ // not supported anymore
38449 //c.el.setStyle("display", "none");
38451 if(north && north.isVisible()){
38452 var b = north.getBox();
38453 var m = north.getMargins();
38454 b.width = w - (m.left+m.right);
38457 centerY = b.height + b.y + m.bottom;
38458 centerH -= centerY;
38459 north.updateBox(this.safeBox(b));
38461 if(south && south.isVisible()){
38462 var b = south.getBox();
38463 var m = south.getMargins();
38464 b.width = w - (m.left+m.right);
38466 var totalHeight = (b.height + m.top + m.bottom);
38467 b.y = h - totalHeight + m.top;
38468 centerH -= totalHeight;
38469 south.updateBox(this.safeBox(b));
38471 if(west && west.isVisible()){
38472 var b = west.getBox();
38473 var m = west.getMargins();
38474 b.height = centerH - (m.top+m.bottom);
38476 b.y = centerY + m.top;
38477 var totalWidth = (b.width + m.left + m.right);
38478 centerX += totalWidth;
38479 centerW -= totalWidth;
38480 west.updateBox(this.safeBox(b));
38482 if(east && east.isVisible()){
38483 var b = east.getBox();
38484 var m = east.getMargins();
38485 b.height = centerH - (m.top+m.bottom);
38486 var totalWidth = (b.width + m.left + m.right);
38487 b.x = w - totalWidth + m.left;
38488 b.y = centerY + m.top;
38489 centerW -= totalWidth;
38490 east.updateBox(this.safeBox(b));
38493 var m = center.getMargins();
38495 x: centerX + m.left,
38496 y: centerY + m.top,
38497 width: centerW - (m.left+m.right),
38498 height: centerH - (m.top+m.bottom)
38500 //if(this.hideOnLayout){
38501 //center.el.setStyle("display", "block");
38503 center.updateBox(this.safeBox(centerBox));
38506 this.fireEvent("layout", this);
38510 safeBox : function(box){
38511 box.width = Math.max(0, box.width);
38512 box.height = Math.max(0, box.height);
38517 * Adds a ContentPanel (or subclass) to this layout.
38518 * @param {String} target The target region key (north, south, east, west or center).
38519 * @param {Roo.ContentPanel} panel The panel to add
38520 * @return {Roo.ContentPanel} The added panel
38522 add : function(target, panel){
38524 target = target.toLowerCase();
38525 return this.regions[target].add(panel);
38529 * Remove a ContentPanel (or subclass) to this layout.
38530 * @param {String} target The target region key (north, south, east, west or center).
38531 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38532 * @return {Roo.ContentPanel} The removed panel
38534 remove : function(target, panel){
38535 target = target.toLowerCase();
38536 return this.regions[target].remove(panel);
38540 * Searches all regions for a panel with the specified id
38541 * @param {String} panelId
38542 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38544 findPanel : function(panelId){
38545 var rs = this.regions;
38546 for(var target in rs){
38547 if(typeof rs[target] != "function"){
38548 var p = rs[target].getPanel(panelId);
38558 * Searches all regions for a panel with the specified id and activates (shows) it.
38559 * @param {String/ContentPanel} panelId The panels id or the panel itself
38560 * @return {Roo.ContentPanel} The shown panel or null
38562 showPanel : function(panelId) {
38563 var rs = this.regions;
38564 for(var target in rs){
38565 var r = rs[target];
38566 if(typeof r != "function"){
38567 if(r.hasPanel(panelId)){
38568 return r.showPanel(panelId);
38576 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38577 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38580 restoreState : function(provider){
38582 provider = Roo.state.Manager;
38584 var sm = new Roo.LayoutStateManager();
38585 sm.init(this, provider);
38591 * Adds a xtype elements to the layout.
38595 xtype : 'ContentPanel',
38602 xtype : 'NestedLayoutPanel',
38608 items : [ ... list of content panels or nested layout panels.. ]
38612 * @param {Object} cfg Xtype definition of item to add.
38614 addxtype : function(cfg)
38616 // basically accepts a pannel...
38617 // can accept a layout region..!?!?
38618 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38621 // theory? children can only be panels??
38623 //if (!cfg.xtype.match(/Panel$/)) {
38628 if (typeof(cfg.region) == 'undefined') {
38629 Roo.log("Failed to add Panel, region was not set");
38633 var region = cfg.region;
38639 xitems = cfg.items;
38644 if ( region == 'center') {
38645 Roo.log("Center: " + cfg.title);
38651 case 'Content': // ContentPanel (el, cfg)
38652 case 'Scroll': // ContentPanel (el, cfg)
38654 cfg.autoCreate = cfg.autoCreate || true;
38655 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38657 // var el = this.el.createChild();
38658 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38661 this.add(region, ret);
38665 case 'TreePanel': // our new panel!
38666 cfg.el = this.el.createChild();
38667 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38668 this.add(region, ret);
38673 // create a new Layout (which is a Border Layout...
38675 var clayout = cfg.layout;
38676 clayout.el = this.el.createChild();
38677 clayout.items = clayout.items || [];
38681 // replace this exitems with the clayout ones..
38682 xitems = clayout.items;
38684 // force background off if it's in center...
38685 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38686 cfg.background = false;
38688 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38691 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38692 //console.log('adding nested layout panel ' + cfg.toSource());
38693 this.add(region, ret);
38694 nb = {}; /// find first...
38699 // needs grid and region
38701 //var el = this.getRegion(region).el.createChild();
38703 *var el = this.el.createChild();
38704 // create the grid first...
38705 cfg.grid.container = el;
38706 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38709 if (region == 'center' && this.active ) {
38710 cfg.background = false;
38713 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38715 this.add(region, ret);
38717 if (cfg.background) {
38718 // render grid on panel activation (if panel background)
38719 ret.on('activate', function(gp) {
38720 if (!gp.grid.rendered) {
38721 // gp.grid.render(el);
38725 // cfg.grid.render(el);
38731 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38732 // it was the old xcomponent building that caused this before.
38733 // espeically if border is the top element in the tree.
38743 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38745 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38746 this.add(region, ret);
38750 throw "Can not add '" + cfg.xtype + "' to Border";
38756 this.beginUpdate();
38760 Roo.each(xitems, function(i) {
38761 region = nb && i.region ? i.region : false;
38763 var add = ret.addxtype(i);
38766 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38767 if (!i.background) {
38768 abn[region] = nb[region] ;
38775 // make the last non-background panel active..
38776 //if (nb) { Roo.log(abn); }
38779 for(var r in abn) {
38780 region = this.getRegion(r);
38782 // tried using nb[r], but it does not work..
38784 region.showPanel(abn[r]);
38795 factory : function(cfg)
38798 var validRegions = Roo.bootstrap.layout.Border.regions;
38800 var target = cfg.region;
38803 var r = Roo.bootstrap.layout;
38807 return new r.North(cfg);
38809 return new r.South(cfg);
38811 return new r.East(cfg);
38813 return new r.West(cfg);
38815 return new r.Center(cfg);
38817 throw 'Layout region "'+target+'" not supported.';
38824 * Ext JS Library 1.1.1
38825 * Copyright(c) 2006-2007, Ext JS, LLC.
38827 * Originally Released Under LGPL - original licence link has changed is not relivant.
38830 * <script type="text/javascript">
38834 * @class Roo.bootstrap.layout.Basic
38835 * @extends Roo.util.Observable
38836 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38837 * and does not have a titlebar, tabs or any other features. All it does is size and position
38838 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38839 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38840 * @cfg {string} region the region that it inhabits..
38841 * @cfg {bool} skipConfig skip config?
38845 Roo.bootstrap.layout.Basic = function(config){
38847 this.mgr = config.mgr;
38849 this.position = config.region;
38851 var skipConfig = config.skipConfig;
38855 * @scope Roo.BasicLayoutRegion
38859 * @event beforeremove
38860 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38861 * @param {Roo.LayoutRegion} this
38862 * @param {Roo.ContentPanel} panel The panel
38863 * @param {Object} e The cancel event object
38865 "beforeremove" : true,
38867 * @event invalidated
38868 * Fires when the layout for this region is changed.
38869 * @param {Roo.LayoutRegion} this
38871 "invalidated" : true,
38873 * @event visibilitychange
38874 * Fires when this region is shown or hidden
38875 * @param {Roo.LayoutRegion} this
38876 * @param {Boolean} visibility true or false
38878 "visibilitychange" : true,
38880 * @event paneladded
38881 * Fires when a panel is added.
38882 * @param {Roo.LayoutRegion} this
38883 * @param {Roo.ContentPanel} panel The panel
38885 "paneladded" : true,
38887 * @event panelremoved
38888 * Fires when a panel is removed.
38889 * @param {Roo.LayoutRegion} this
38890 * @param {Roo.ContentPanel} panel The panel
38892 "panelremoved" : true,
38894 * @event beforecollapse
38895 * Fires when this region before collapse.
38896 * @param {Roo.LayoutRegion} this
38898 "beforecollapse" : true,
38901 * Fires when this region is collapsed.
38902 * @param {Roo.LayoutRegion} this
38904 "collapsed" : true,
38907 * Fires when this region is expanded.
38908 * @param {Roo.LayoutRegion} this
38913 * Fires when this region is slid into view.
38914 * @param {Roo.LayoutRegion} this
38916 "slideshow" : true,
38919 * Fires when this region slides out of view.
38920 * @param {Roo.LayoutRegion} this
38922 "slidehide" : true,
38924 * @event panelactivated
38925 * Fires when a panel is activated.
38926 * @param {Roo.LayoutRegion} this
38927 * @param {Roo.ContentPanel} panel The activated panel
38929 "panelactivated" : true,
38932 * Fires when the user resizes this region.
38933 * @param {Roo.LayoutRegion} this
38934 * @param {Number} newSize The new size (width for east/west, height for north/south)
38938 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38939 this.panels = new Roo.util.MixedCollection();
38940 this.panels.getKey = this.getPanelId.createDelegate(this);
38942 this.activePanel = null;
38943 // ensure listeners are added...
38945 if (config.listeners || config.events) {
38946 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38947 listeners : config.listeners || {},
38948 events : config.events || {}
38952 if(skipConfig !== true){
38953 this.applyConfig(config);
38957 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38959 getPanelId : function(p){
38963 applyConfig : function(config){
38964 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38965 this.config = config;
38970 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38971 * the width, for horizontal (north, south) the height.
38972 * @param {Number} newSize The new width or height
38974 resizeTo : function(newSize){
38975 var el = this.el ? this.el :
38976 (this.activePanel ? this.activePanel.getEl() : null);
38978 switch(this.position){
38981 el.setWidth(newSize);
38982 this.fireEvent("resized", this, newSize);
38986 el.setHeight(newSize);
38987 this.fireEvent("resized", this, newSize);
38993 getBox : function(){
38994 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38997 getMargins : function(){
38998 return this.margins;
39001 updateBox : function(box){
39003 var el = this.activePanel.getEl();
39004 el.dom.style.left = box.x + "px";
39005 el.dom.style.top = box.y + "px";
39006 this.activePanel.setSize(box.width, box.height);
39010 * Returns the container element for this region.
39011 * @return {Roo.Element}
39013 getEl : function(){
39014 return this.activePanel;
39018 * Returns true if this region is currently visible.
39019 * @return {Boolean}
39021 isVisible : function(){
39022 return this.activePanel ? true : false;
39025 setActivePanel : function(panel){
39026 panel = this.getPanel(panel);
39027 if(this.activePanel && this.activePanel != panel){
39028 this.activePanel.setActiveState(false);
39029 this.activePanel.getEl().setLeftTop(-10000,-10000);
39031 this.activePanel = panel;
39032 panel.setActiveState(true);
39034 panel.setSize(this.box.width, this.box.height);
39036 this.fireEvent("panelactivated", this, panel);
39037 this.fireEvent("invalidated");
39041 * Show the specified panel.
39042 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39043 * @return {Roo.ContentPanel} The shown panel or null
39045 showPanel : function(panel){
39046 panel = this.getPanel(panel);
39048 this.setActivePanel(panel);
39054 * Get the active panel for this region.
39055 * @return {Roo.ContentPanel} The active panel or null
39057 getActivePanel : function(){
39058 return this.activePanel;
39062 * Add the passed ContentPanel(s)
39063 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39064 * @return {Roo.ContentPanel} The panel added (if only one was added)
39066 add : function(panel){
39067 if(arguments.length > 1){
39068 for(var i = 0, len = arguments.length; i < len; i++) {
39069 this.add(arguments[i]);
39073 if(this.hasPanel(panel)){
39074 this.showPanel(panel);
39077 var el = panel.getEl();
39078 if(el.dom.parentNode != this.mgr.el.dom){
39079 this.mgr.el.dom.appendChild(el.dom);
39081 if(panel.setRegion){
39082 panel.setRegion(this);
39084 this.panels.add(panel);
39085 el.setStyle("position", "absolute");
39086 if(!panel.background){
39087 this.setActivePanel(panel);
39088 if(this.config.initialSize && this.panels.getCount()==1){
39089 this.resizeTo(this.config.initialSize);
39092 this.fireEvent("paneladded", this, panel);
39097 * Returns true if the panel is in this region.
39098 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39099 * @return {Boolean}
39101 hasPanel : function(panel){
39102 if(typeof panel == "object"){ // must be panel obj
39103 panel = panel.getId();
39105 return this.getPanel(panel) ? true : false;
39109 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39110 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39111 * @param {Boolean} preservePanel Overrides the config preservePanel option
39112 * @return {Roo.ContentPanel} The panel that was removed
39114 remove : function(panel, preservePanel){
39115 panel = this.getPanel(panel);
39120 this.fireEvent("beforeremove", this, panel, e);
39121 if(e.cancel === true){
39124 var panelId = panel.getId();
39125 this.panels.removeKey(panelId);
39130 * Returns the panel specified or null if it's not in this region.
39131 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39132 * @return {Roo.ContentPanel}
39134 getPanel : function(id){
39135 if(typeof id == "object"){ // must be panel obj
39138 return this.panels.get(id);
39142 * Returns this regions position (north/south/east/west/center).
39145 getPosition: function(){
39146 return this.position;
39150 * Ext JS Library 1.1.1
39151 * Copyright(c) 2006-2007, Ext JS, LLC.
39153 * Originally Released Under LGPL - original licence link has changed is not relivant.
39156 * <script type="text/javascript">
39160 * @class Roo.bootstrap.layout.Region
39161 * @extends Roo.bootstrap.layout.Basic
39162 * This class represents a region in a layout manager.
39164 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39165 * @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})
39166 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39167 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39168 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39169 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39170 * @cfg {String} title The title for the region (overrides panel titles)
39171 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39172 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39173 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39174 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39175 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39176 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39177 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39178 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39179 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39180 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39182 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39183 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39184 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39185 * @cfg {Number} width For East/West panels
39186 * @cfg {Number} height For North/South panels
39187 * @cfg {Boolean} split To show the splitter
39188 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39190 * @cfg {string} cls Extra CSS classes to add to region
39192 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39193 * @cfg {string} region the region that it inhabits..
39196 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39197 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39199 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39200 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39201 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39203 Roo.bootstrap.layout.Region = function(config)
39205 this.applyConfig(config);
39207 var mgr = config.mgr;
39208 var pos = config.region;
39209 config.skipConfig = true;
39210 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39213 this.onRender(mgr.el);
39216 this.visible = true;
39217 this.collapsed = false;
39218 this.unrendered_panels = [];
39221 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39223 position: '', // set by wrapper (eg. north/south etc..)
39224 unrendered_panels : null, // unrendered panels.
39226 tabPosition : false,
39228 mgr: false, // points to 'Border'
39231 createBody : function(){
39232 /** This region's body element
39233 * @type Roo.Element */
39234 this.bodyEl = this.el.createChild({
39236 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39240 onRender: function(ctr, pos)
39242 var dh = Roo.DomHelper;
39243 /** This region's container element
39244 * @type Roo.Element */
39245 this.el = dh.append(ctr.dom, {
39247 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39249 /** This region's title element
39250 * @type Roo.Element */
39252 this.titleEl = dh.append(this.el.dom, {
39254 unselectable: "on",
39255 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39257 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39258 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39262 this.titleEl.enableDisplayMode();
39263 /** This region's title text element
39264 * @type HTMLElement */
39265 this.titleTextEl = this.titleEl.dom.firstChild;
39266 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39268 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39269 this.closeBtn.enableDisplayMode();
39270 this.closeBtn.on("click", this.closeClicked, this);
39271 this.closeBtn.hide();
39273 this.createBody(this.config);
39274 if(this.config.hideWhenEmpty){
39276 this.on("paneladded", this.validateVisibility, this);
39277 this.on("panelremoved", this.validateVisibility, this);
39279 if(this.autoScroll){
39280 this.bodyEl.setStyle("overflow", "auto");
39282 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39284 //if(c.titlebar !== false){
39285 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39286 this.titleEl.hide();
39288 this.titleEl.show();
39289 if(this.config.title){
39290 this.titleTextEl.innerHTML = this.config.title;
39294 if(this.config.collapsed){
39295 this.collapse(true);
39297 if(this.config.hidden){
39301 if (this.unrendered_panels && this.unrendered_panels.length) {
39302 for (var i =0;i< this.unrendered_panels.length; i++) {
39303 this.add(this.unrendered_panels[i]);
39305 this.unrendered_panels = null;
39311 applyConfig : function(c)
39314 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39315 var dh = Roo.DomHelper;
39316 if(c.titlebar !== false){
39317 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39318 this.collapseBtn.on("click", this.collapse, this);
39319 this.collapseBtn.enableDisplayMode();
39321 if(c.showPin === true || this.showPin){
39322 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39323 this.stickBtn.enableDisplayMode();
39324 this.stickBtn.on("click", this.expand, this);
39325 this.stickBtn.hide();
39330 /** This region's collapsed element
39331 * @type Roo.Element */
39334 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39335 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39338 if(c.floatable !== false){
39339 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39340 this.collapsedEl.on("click", this.collapseClick, this);
39343 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39344 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39345 id: "message", unselectable: "on", style:{"float":"left"}});
39346 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39348 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39349 this.expandBtn.on("click", this.expand, this);
39353 if(this.collapseBtn){
39354 this.collapseBtn.setVisible(c.collapsible == true);
39357 this.cmargins = c.cmargins || this.cmargins ||
39358 (this.position == "west" || this.position == "east" ?
39359 {top: 0, left: 2, right:2, bottom: 0} :
39360 {top: 2, left: 0, right:0, bottom: 2});
39362 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39365 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39367 this.autoScroll = c.autoScroll || false;
39372 this.duration = c.duration || .30;
39373 this.slideDuration = c.slideDuration || .45;
39378 * Returns true if this region is currently visible.
39379 * @return {Boolean}
39381 isVisible : function(){
39382 return this.visible;
39386 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39387 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39389 //setCollapsedTitle : function(title){
39390 // title = title || " ";
39391 // if(this.collapsedTitleTextEl){
39392 // this.collapsedTitleTextEl.innerHTML = title;
39396 getBox : function(){
39398 // if(!this.collapsed){
39399 b = this.el.getBox(false, true);
39401 // b = this.collapsedEl.getBox(false, true);
39406 getMargins : function(){
39407 return this.margins;
39408 //return this.collapsed ? this.cmargins : this.margins;
39411 highlight : function(){
39412 this.el.addClass("x-layout-panel-dragover");
39415 unhighlight : function(){
39416 this.el.removeClass("x-layout-panel-dragover");
39419 updateBox : function(box)
39421 if (!this.bodyEl) {
39422 return; // not rendered yet..
39426 if(!this.collapsed){
39427 this.el.dom.style.left = box.x + "px";
39428 this.el.dom.style.top = box.y + "px";
39429 this.updateBody(box.width, box.height);
39431 this.collapsedEl.dom.style.left = box.x + "px";
39432 this.collapsedEl.dom.style.top = box.y + "px";
39433 this.collapsedEl.setSize(box.width, box.height);
39436 this.tabs.autoSizeTabs();
39440 updateBody : function(w, h)
39443 this.el.setWidth(w);
39444 w -= this.el.getBorderWidth("rl");
39445 if(this.config.adjustments){
39446 w += this.config.adjustments[0];
39449 if(h !== null && h > 0){
39450 this.el.setHeight(h);
39451 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39452 h -= this.el.getBorderWidth("tb");
39453 if(this.config.adjustments){
39454 h += this.config.adjustments[1];
39456 this.bodyEl.setHeight(h);
39458 h = this.tabs.syncHeight(h);
39461 if(this.panelSize){
39462 w = w !== null ? w : this.panelSize.width;
39463 h = h !== null ? h : this.panelSize.height;
39465 if(this.activePanel){
39466 var el = this.activePanel.getEl();
39467 w = w !== null ? w : el.getWidth();
39468 h = h !== null ? h : el.getHeight();
39469 this.panelSize = {width: w, height: h};
39470 this.activePanel.setSize(w, h);
39472 if(Roo.isIE && this.tabs){
39473 this.tabs.el.repaint();
39478 * Returns the container element for this region.
39479 * @return {Roo.Element}
39481 getEl : function(){
39486 * Hides this region.
39489 //if(!this.collapsed){
39490 this.el.dom.style.left = "-2000px";
39493 // this.collapsedEl.dom.style.left = "-2000px";
39494 // this.collapsedEl.hide();
39496 this.visible = false;
39497 this.fireEvent("visibilitychange", this, false);
39501 * Shows this region if it was previously hidden.
39504 //if(!this.collapsed){
39507 // this.collapsedEl.show();
39509 this.visible = true;
39510 this.fireEvent("visibilitychange", this, true);
39513 closeClicked : function(){
39514 if(this.activePanel){
39515 this.remove(this.activePanel);
39519 collapseClick : function(e){
39521 e.stopPropagation();
39524 e.stopPropagation();
39530 * Collapses this region.
39531 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39534 collapse : function(skipAnim, skipCheck = false){
39535 if(this.collapsed) {
39539 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39541 this.collapsed = true;
39543 this.split.el.hide();
39545 if(this.config.animate && skipAnim !== true){
39546 this.fireEvent("invalidated", this);
39547 this.animateCollapse();
39549 this.el.setLocation(-20000,-20000);
39551 this.collapsedEl.show();
39552 this.fireEvent("collapsed", this);
39553 this.fireEvent("invalidated", this);
39559 animateCollapse : function(){
39564 * Expands this region if it was previously collapsed.
39565 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39566 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39569 expand : function(e, skipAnim){
39571 e.stopPropagation();
39573 if(!this.collapsed || this.el.hasActiveFx()) {
39577 this.afterSlideIn();
39580 this.collapsed = false;
39581 if(this.config.animate && skipAnim !== true){
39582 this.animateExpand();
39586 this.split.el.show();
39588 this.collapsedEl.setLocation(-2000,-2000);
39589 this.collapsedEl.hide();
39590 this.fireEvent("invalidated", this);
39591 this.fireEvent("expanded", this);
39595 animateExpand : function(){
39599 initTabs : function()
39601 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39603 var ts = new Roo.bootstrap.panel.Tabs({
39604 el: this.bodyEl.dom,
39606 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39607 disableTooltips: this.config.disableTabTips,
39608 toolbar : this.config.toolbar
39611 if(this.config.hideTabs){
39612 ts.stripWrap.setDisplayed(false);
39615 ts.resizeTabs = this.config.resizeTabs === true;
39616 ts.minTabWidth = this.config.minTabWidth || 40;
39617 ts.maxTabWidth = this.config.maxTabWidth || 250;
39618 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39619 ts.monitorResize = false;
39620 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39621 ts.bodyEl.addClass('roo-layout-tabs-body');
39622 this.panels.each(this.initPanelAsTab, this);
39625 initPanelAsTab : function(panel){
39626 var ti = this.tabs.addTab(
39630 this.config.closeOnTab && panel.isClosable(),
39633 if(panel.tabTip !== undefined){
39634 ti.setTooltip(panel.tabTip);
39636 ti.on("activate", function(){
39637 this.setActivePanel(panel);
39640 if(this.config.closeOnTab){
39641 ti.on("beforeclose", function(t, e){
39643 this.remove(panel);
39647 panel.tabItem = ti;
39652 updatePanelTitle : function(panel, title)
39654 if(this.activePanel == panel){
39655 this.updateTitle(title);
39658 var ti = this.tabs.getTab(panel.getEl().id);
39660 if(panel.tabTip !== undefined){
39661 ti.setTooltip(panel.tabTip);
39666 updateTitle : function(title){
39667 if(this.titleTextEl && !this.config.title){
39668 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39672 setActivePanel : function(panel)
39674 panel = this.getPanel(panel);
39675 if(this.activePanel && this.activePanel != panel){
39676 if(this.activePanel.setActiveState(false) === false){
39680 this.activePanel = panel;
39681 panel.setActiveState(true);
39682 if(this.panelSize){
39683 panel.setSize(this.panelSize.width, this.panelSize.height);
39686 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39688 this.updateTitle(panel.getTitle());
39690 this.fireEvent("invalidated", this);
39692 this.fireEvent("panelactivated", this, panel);
39696 * Shows the specified panel.
39697 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39698 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39700 showPanel : function(panel)
39702 panel = this.getPanel(panel);
39705 var tab = this.tabs.getTab(panel.getEl().id);
39706 if(tab.isHidden()){
39707 this.tabs.unhideTab(tab.id);
39711 this.setActivePanel(panel);
39718 * Get the active panel for this region.
39719 * @return {Roo.ContentPanel} The active panel or null
39721 getActivePanel : function(){
39722 return this.activePanel;
39725 validateVisibility : function(){
39726 if(this.panels.getCount() < 1){
39727 this.updateTitle(" ");
39728 this.closeBtn.hide();
39731 if(!this.isVisible()){
39738 * Adds the passed ContentPanel(s) to this region.
39739 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39740 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39742 add : function(panel)
39744 if(arguments.length > 1){
39745 for(var i = 0, len = arguments.length; i < len; i++) {
39746 this.add(arguments[i]);
39751 // if we have not been rendered yet, then we can not really do much of this..
39752 if (!this.bodyEl) {
39753 this.unrendered_panels.push(panel);
39760 if(this.hasPanel(panel)){
39761 this.showPanel(panel);
39764 panel.setRegion(this);
39765 this.panels.add(panel);
39766 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39767 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39768 // and hide them... ???
39769 this.bodyEl.dom.appendChild(panel.getEl().dom);
39770 if(panel.background !== true){
39771 this.setActivePanel(panel);
39773 this.fireEvent("paneladded", this, panel);
39780 this.initPanelAsTab(panel);
39784 if(panel.background !== true){
39785 this.tabs.activate(panel.getEl().id);
39787 this.fireEvent("paneladded", this, panel);
39792 * Hides the tab for the specified panel.
39793 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39795 hidePanel : function(panel){
39796 if(this.tabs && (panel = this.getPanel(panel))){
39797 this.tabs.hideTab(panel.getEl().id);
39802 * Unhides the tab for a previously hidden panel.
39803 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39805 unhidePanel : function(panel){
39806 if(this.tabs && (panel = this.getPanel(panel))){
39807 this.tabs.unhideTab(panel.getEl().id);
39811 clearPanels : function(){
39812 while(this.panels.getCount() > 0){
39813 this.remove(this.panels.first());
39818 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39819 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39820 * @param {Boolean} preservePanel Overrides the config preservePanel option
39821 * @return {Roo.ContentPanel} The panel that was removed
39823 remove : function(panel, preservePanel)
39825 panel = this.getPanel(panel);
39830 this.fireEvent("beforeremove", this, panel, e);
39831 if(e.cancel === true){
39834 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39835 var panelId = panel.getId();
39836 this.panels.removeKey(panelId);
39838 document.body.appendChild(panel.getEl().dom);
39841 this.tabs.removeTab(panel.getEl().id);
39842 }else if (!preservePanel){
39843 this.bodyEl.dom.removeChild(panel.getEl().dom);
39845 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39846 var p = this.panels.first();
39847 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39848 tempEl.appendChild(p.getEl().dom);
39849 this.bodyEl.update("");
39850 this.bodyEl.dom.appendChild(p.getEl().dom);
39852 this.updateTitle(p.getTitle());
39854 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39855 this.setActivePanel(p);
39857 panel.setRegion(null);
39858 if(this.activePanel == panel){
39859 this.activePanel = null;
39861 if(this.config.autoDestroy !== false && preservePanel !== true){
39862 try{panel.destroy();}catch(e){}
39864 this.fireEvent("panelremoved", this, panel);
39869 * Returns the TabPanel component used by this region
39870 * @return {Roo.TabPanel}
39872 getTabs : function(){
39876 createTool : function(parentEl, className){
39877 var btn = Roo.DomHelper.append(parentEl, {
39879 cls: "x-layout-tools-button",
39882 cls: "roo-layout-tools-button-inner " + className,
39886 btn.addClassOnOver("roo-layout-tools-button-over");
39891 * Ext JS Library 1.1.1
39892 * Copyright(c) 2006-2007, Ext JS, LLC.
39894 * Originally Released Under LGPL - original licence link has changed is not relivant.
39897 * <script type="text/javascript">
39903 * @class Roo.SplitLayoutRegion
39904 * @extends Roo.LayoutRegion
39905 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39907 Roo.bootstrap.layout.Split = function(config){
39908 this.cursor = config.cursor;
39909 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39912 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39914 splitTip : "Drag to resize.",
39915 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39916 useSplitTips : false,
39918 applyConfig : function(config){
39919 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39922 onRender : function(ctr,pos) {
39924 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39925 if(!this.config.split){
39930 var splitEl = Roo.DomHelper.append(ctr.dom, {
39932 id: this.el.id + "-split",
39933 cls: "roo-layout-split roo-layout-split-"+this.position,
39936 /** The SplitBar for this region
39937 * @type Roo.SplitBar */
39938 // does not exist yet...
39939 Roo.log([this.position, this.orientation]);
39941 this.split = new Roo.bootstrap.SplitBar({
39942 dragElement : splitEl,
39943 resizingElement: this.el,
39944 orientation : this.orientation
39947 this.split.on("moved", this.onSplitMove, this);
39948 this.split.useShim = this.config.useShim === true;
39949 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39950 if(this.useSplitTips){
39951 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39953 //if(config.collapsible){
39954 // this.split.el.on("dblclick", this.collapse, this);
39957 if(typeof this.config.minSize != "undefined"){
39958 this.split.minSize = this.config.minSize;
39960 if(typeof this.config.maxSize != "undefined"){
39961 this.split.maxSize = this.config.maxSize;
39963 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39964 this.hideSplitter();
39969 getHMaxSize : function(){
39970 var cmax = this.config.maxSize || 10000;
39971 var center = this.mgr.getRegion("center");
39972 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39975 getVMaxSize : function(){
39976 var cmax = this.config.maxSize || 10000;
39977 var center = this.mgr.getRegion("center");
39978 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39981 onSplitMove : function(split, newSize){
39982 this.fireEvent("resized", this, newSize);
39986 * Returns the {@link Roo.SplitBar} for this region.
39987 * @return {Roo.SplitBar}
39989 getSplitBar : function(){
39994 this.hideSplitter();
39995 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39998 hideSplitter : function(){
40000 this.split.el.setLocation(-2000,-2000);
40001 this.split.el.hide();
40007 this.split.el.show();
40009 Roo.bootstrap.layout.Split.superclass.show.call(this);
40012 beforeSlide: function(){
40013 if(Roo.isGecko){// firefox overflow auto bug workaround
40014 this.bodyEl.clip();
40016 this.tabs.bodyEl.clip();
40018 if(this.activePanel){
40019 this.activePanel.getEl().clip();
40021 if(this.activePanel.beforeSlide){
40022 this.activePanel.beforeSlide();
40028 afterSlide : function(){
40029 if(Roo.isGecko){// firefox overflow auto bug workaround
40030 this.bodyEl.unclip();
40032 this.tabs.bodyEl.unclip();
40034 if(this.activePanel){
40035 this.activePanel.getEl().unclip();
40036 if(this.activePanel.afterSlide){
40037 this.activePanel.afterSlide();
40043 initAutoHide : function(){
40044 if(this.autoHide !== false){
40045 if(!this.autoHideHd){
40046 var st = new Roo.util.DelayedTask(this.slideIn, this);
40047 this.autoHideHd = {
40048 "mouseout": function(e){
40049 if(!e.within(this.el, true)){
40053 "mouseover" : function(e){
40059 this.el.on(this.autoHideHd);
40063 clearAutoHide : function(){
40064 if(this.autoHide !== false){
40065 this.el.un("mouseout", this.autoHideHd.mouseout);
40066 this.el.un("mouseover", this.autoHideHd.mouseover);
40070 clearMonitor : function(){
40071 Roo.get(document).un("click", this.slideInIf, this);
40074 // these names are backwards but not changed for compat
40075 slideOut : function(){
40076 if(this.isSlid || this.el.hasActiveFx()){
40079 this.isSlid = true;
40080 if(this.collapseBtn){
40081 this.collapseBtn.hide();
40083 this.closeBtnState = this.closeBtn.getStyle('display');
40084 this.closeBtn.hide();
40086 this.stickBtn.show();
40089 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40090 this.beforeSlide();
40091 this.el.setStyle("z-index", 10001);
40092 this.el.slideIn(this.getSlideAnchor(), {
40093 callback: function(){
40095 this.initAutoHide();
40096 Roo.get(document).on("click", this.slideInIf, this);
40097 this.fireEvent("slideshow", this);
40104 afterSlideIn : function(){
40105 this.clearAutoHide();
40106 this.isSlid = false;
40107 this.clearMonitor();
40108 this.el.setStyle("z-index", "");
40109 if(this.collapseBtn){
40110 this.collapseBtn.show();
40112 this.closeBtn.setStyle('display', this.closeBtnState);
40114 this.stickBtn.hide();
40116 this.fireEvent("slidehide", this);
40119 slideIn : function(cb){
40120 if(!this.isSlid || this.el.hasActiveFx()){
40124 this.isSlid = false;
40125 this.beforeSlide();
40126 this.el.slideOut(this.getSlideAnchor(), {
40127 callback: function(){
40128 this.el.setLeftTop(-10000, -10000);
40130 this.afterSlideIn();
40138 slideInIf : function(e){
40139 if(!e.within(this.el)){
40144 animateCollapse : function(){
40145 this.beforeSlide();
40146 this.el.setStyle("z-index", 20000);
40147 var anchor = this.getSlideAnchor();
40148 this.el.slideOut(anchor, {
40149 callback : function(){
40150 this.el.setStyle("z-index", "");
40151 this.collapsedEl.slideIn(anchor, {duration:.3});
40153 this.el.setLocation(-10000,-10000);
40155 this.fireEvent("collapsed", this);
40162 animateExpand : function(){
40163 this.beforeSlide();
40164 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40165 this.el.setStyle("z-index", 20000);
40166 this.collapsedEl.hide({
40169 this.el.slideIn(this.getSlideAnchor(), {
40170 callback : function(){
40171 this.el.setStyle("z-index", "");
40174 this.split.el.show();
40176 this.fireEvent("invalidated", this);
40177 this.fireEvent("expanded", this);
40205 getAnchor : function(){
40206 return this.anchors[this.position];
40209 getCollapseAnchor : function(){
40210 return this.canchors[this.position];
40213 getSlideAnchor : function(){
40214 return this.sanchors[this.position];
40217 getAlignAdj : function(){
40218 var cm = this.cmargins;
40219 switch(this.position){
40235 getExpandAdj : function(){
40236 var c = this.collapsedEl, cm = this.cmargins;
40237 switch(this.position){
40239 return [-(cm.right+c.getWidth()+cm.left), 0];
40242 return [cm.right+c.getWidth()+cm.left, 0];
40245 return [0, -(cm.top+cm.bottom+c.getHeight())];
40248 return [0, cm.top+cm.bottom+c.getHeight()];
40254 * Ext JS Library 1.1.1
40255 * Copyright(c) 2006-2007, Ext JS, LLC.
40257 * Originally Released Under LGPL - original licence link has changed is not relivant.
40260 * <script type="text/javascript">
40263 * These classes are private internal classes
40265 Roo.bootstrap.layout.Center = function(config){
40266 config.region = "center";
40267 Roo.bootstrap.layout.Region.call(this, config);
40268 this.visible = true;
40269 this.minWidth = config.minWidth || 20;
40270 this.minHeight = config.minHeight || 20;
40273 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40275 // center panel can't be hidden
40279 // center panel can't be hidden
40282 getMinWidth: function(){
40283 return this.minWidth;
40286 getMinHeight: function(){
40287 return this.minHeight;
40301 Roo.bootstrap.layout.North = function(config)
40303 config.region = 'north';
40304 config.cursor = 'n-resize';
40306 Roo.bootstrap.layout.Split.call(this, config);
40310 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40311 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40312 this.split.el.addClass("roo-layout-split-v");
40314 //var size = config.initialSize || config.height;
40315 //if(this.el && typeof size != "undefined"){
40316 // this.el.setHeight(size);
40319 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40321 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40324 onRender : function(ctr, pos)
40326 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40327 var size = this.config.initialSize || this.config.height;
40328 if(this.el && typeof size != "undefined"){
40329 this.el.setHeight(size);
40334 getBox : function(){
40335 if(this.collapsed){
40336 return this.collapsedEl.getBox();
40338 var box = this.el.getBox();
40340 box.height += this.split.el.getHeight();
40345 updateBox : function(box){
40346 if(this.split && !this.collapsed){
40347 box.height -= this.split.el.getHeight();
40348 this.split.el.setLeft(box.x);
40349 this.split.el.setTop(box.y+box.height);
40350 this.split.el.setWidth(box.width);
40352 if(this.collapsed){
40353 this.updateBody(box.width, null);
40355 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40363 Roo.bootstrap.layout.South = function(config){
40364 config.region = 'south';
40365 config.cursor = 's-resize';
40366 Roo.bootstrap.layout.Split.call(this, config);
40368 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40369 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40370 this.split.el.addClass("roo-layout-split-v");
40375 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40376 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40378 onRender : function(ctr, pos)
40380 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40381 var size = this.config.initialSize || this.config.height;
40382 if(this.el && typeof size != "undefined"){
40383 this.el.setHeight(size);
40388 getBox : function(){
40389 if(this.collapsed){
40390 return this.collapsedEl.getBox();
40392 var box = this.el.getBox();
40394 var sh = this.split.el.getHeight();
40401 updateBox : function(box){
40402 if(this.split && !this.collapsed){
40403 var sh = this.split.el.getHeight();
40406 this.split.el.setLeft(box.x);
40407 this.split.el.setTop(box.y-sh);
40408 this.split.el.setWidth(box.width);
40410 if(this.collapsed){
40411 this.updateBody(box.width, null);
40413 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40417 Roo.bootstrap.layout.East = function(config){
40418 config.region = "east";
40419 config.cursor = "e-resize";
40420 Roo.bootstrap.layout.Split.call(this, config);
40422 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40423 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40424 this.split.el.addClass("roo-layout-split-h");
40428 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40429 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40431 onRender : function(ctr, pos)
40433 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40434 var size = this.config.initialSize || this.config.width;
40435 if(this.el && typeof size != "undefined"){
40436 this.el.setWidth(size);
40441 getBox : function(){
40442 if(this.collapsed){
40443 return this.collapsedEl.getBox();
40445 var box = this.el.getBox();
40447 var sw = this.split.el.getWidth();
40454 updateBox : function(box){
40455 if(this.split && !this.collapsed){
40456 var sw = this.split.el.getWidth();
40458 this.split.el.setLeft(box.x);
40459 this.split.el.setTop(box.y);
40460 this.split.el.setHeight(box.height);
40463 if(this.collapsed){
40464 this.updateBody(null, box.height);
40466 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40470 Roo.bootstrap.layout.West = function(config){
40471 config.region = "west";
40472 config.cursor = "w-resize";
40474 Roo.bootstrap.layout.Split.call(this, config);
40476 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40477 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40478 this.split.el.addClass("roo-layout-split-h");
40482 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40483 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40485 onRender: function(ctr, pos)
40487 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40488 var size = this.config.initialSize || this.config.width;
40489 if(typeof size != "undefined"){
40490 this.el.setWidth(size);
40494 getBox : function(){
40495 if(this.collapsed){
40496 return this.collapsedEl.getBox();
40498 var box = this.el.getBox();
40499 if (box.width == 0) {
40500 box.width = this.config.width; // kludge?
40503 box.width += this.split.el.getWidth();
40508 updateBox : function(box){
40509 if(this.split && !this.collapsed){
40510 var sw = this.split.el.getWidth();
40512 this.split.el.setLeft(box.x+box.width);
40513 this.split.el.setTop(box.y);
40514 this.split.el.setHeight(box.height);
40516 if(this.collapsed){
40517 this.updateBody(null, box.height);
40519 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40521 });Roo.namespace("Roo.bootstrap.panel");/*
40523 * Ext JS Library 1.1.1
40524 * Copyright(c) 2006-2007, Ext JS, LLC.
40526 * Originally Released Under LGPL - original licence link has changed is not relivant.
40529 * <script type="text/javascript">
40532 * @class Roo.ContentPanel
40533 * @extends Roo.util.Observable
40534 * A basic ContentPanel element.
40535 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40536 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40537 * @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
40538 * @cfg {Boolean} closable True if the panel can be closed/removed
40539 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40540 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40541 * @cfg {Toolbar} toolbar A toolbar for this panel
40542 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40543 * @cfg {String} title The title for this panel
40544 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40545 * @cfg {String} url Calls {@link #setUrl} with this value
40546 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40547 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40548 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40549 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40550 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40551 * @cfg {Boolean} badges render the badges
40552 * @cfg {String} cls extra classes to use
40553 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40556 * Create a new ContentPanel.
40557 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40558 * @param {String/Object} config A string to set only the title or a config object
40559 * @param {String} content (optional) Set the HTML content for this panel
40560 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40562 Roo.bootstrap.panel.Content = function( config){
40564 this.tpl = config.tpl || false;
40566 var el = config.el;
40567 var content = config.content;
40569 if(config.autoCreate){ // xtype is available if this is called from factory
40572 this.el = Roo.get(el);
40573 if(!this.el && config && config.autoCreate){
40574 if(typeof config.autoCreate == "object"){
40575 if(!config.autoCreate.id){
40576 config.autoCreate.id = config.id||el;
40578 this.el = Roo.DomHelper.append(document.body,
40579 config.autoCreate, true);
40583 cls: (config.cls || '') +
40584 (config.background ? ' bg-' + config.background : '') +
40585 " roo-layout-inactive-content",
40588 if (config.iframe) {
40592 style : 'border: 0px',
40593 src : 'about:blank'
40599 elcfg.html = config.html;
40603 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40604 if (config.iframe) {
40605 this.iframeEl = this.el.select('iframe',true).first();
40610 this.closable = false;
40611 this.loaded = false;
40612 this.active = false;
40615 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40617 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40619 this.wrapEl = this.el; //this.el.wrap();
40621 if (config.toolbar.items) {
40622 ti = config.toolbar.items ;
40623 delete config.toolbar.items ;
40627 this.toolbar.render(this.wrapEl, 'before');
40628 for(var i =0;i < ti.length;i++) {
40629 // Roo.log(['add child', items[i]]);
40630 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40632 this.toolbar.items = nitems;
40633 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40634 delete config.toolbar;
40638 // xtype created footer. - not sure if will work as we normally have to render first..
40639 if (this.footer && !this.footer.el && this.footer.xtype) {
40640 if (!this.wrapEl) {
40641 this.wrapEl = this.el.wrap();
40644 this.footer.container = this.wrapEl.createChild();
40646 this.footer = Roo.factory(this.footer, Roo);
40651 if(typeof config == "string"){
40652 this.title = config;
40654 Roo.apply(this, config);
40658 this.resizeEl = Roo.get(this.resizeEl, true);
40660 this.resizeEl = this.el;
40662 // handle view.xtype
40670 * Fires when this panel is activated.
40671 * @param {Roo.ContentPanel} this
40675 * @event deactivate
40676 * Fires when this panel is activated.
40677 * @param {Roo.ContentPanel} this
40679 "deactivate" : true,
40683 * Fires when this panel is resized if fitToFrame is true.
40684 * @param {Roo.ContentPanel} this
40685 * @param {Number} width The width after any component adjustments
40686 * @param {Number} height The height after any component adjustments
40692 * Fires when this tab is created
40693 * @param {Roo.ContentPanel} this
40699 * Fires when this content is scrolled
40700 * @param {Roo.ContentPanel} this
40701 * @param {Event} scrollEvent
40712 if(this.autoScroll && !this.iframe){
40713 this.resizeEl.setStyle("overflow", "auto");
40714 this.resizeEl.on('scroll', this.onScroll, this);
40716 // fix randome scrolling
40717 //this.el.on('scroll', function() {
40718 // Roo.log('fix random scolling');
40719 // this.scrollTo('top',0);
40722 content = content || this.content;
40724 this.setContent(content);
40726 if(config && config.url){
40727 this.setUrl(this.url, this.params, this.loadOnce);
40732 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40734 if (this.view && typeof(this.view.xtype) != 'undefined') {
40735 this.view.el = this.el.appendChild(document.createElement("div"));
40736 this.view = Roo.factory(this.view);
40737 this.view.render && this.view.render(false, '');
40741 this.fireEvent('render', this);
40744 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40754 /* Resize Element - use this to work out scroll etc. */
40757 setRegion : function(region){
40758 this.region = region;
40759 this.setActiveClass(region && !this.background);
40763 setActiveClass: function(state)
40766 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40767 this.el.setStyle('position','relative');
40769 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40770 this.el.setStyle('position', 'absolute');
40775 * Returns the toolbar for this Panel if one was configured.
40776 * @return {Roo.Toolbar}
40778 getToolbar : function(){
40779 return this.toolbar;
40782 setActiveState : function(active)
40784 this.active = active;
40785 this.setActiveClass(active);
40787 if(this.fireEvent("deactivate", this) === false){
40792 this.fireEvent("activate", this);
40796 * Updates this panel's element (not for iframe)
40797 * @param {String} content The new content
40798 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40800 setContent : function(content, loadScripts){
40805 this.el.update(content, loadScripts);
40808 ignoreResize : function(w, h){
40809 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40812 this.lastSize = {width: w, height: h};
40817 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40818 * @return {Roo.UpdateManager} The UpdateManager
40820 getUpdateManager : function(){
40824 return this.el.getUpdateManager();
40827 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40828 * Does not work with IFRAME contents
40829 * @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:
40832 url: "your-url.php",
40833 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40834 callback: yourFunction,
40835 scope: yourObject, //(optional scope)
40838 text: "Loading...",
40844 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40845 * 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.
40846 * @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}
40847 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40848 * @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.
40849 * @return {Roo.ContentPanel} this
40857 var um = this.el.getUpdateManager();
40858 um.update.apply(um, arguments);
40864 * 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.
40865 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40866 * @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)
40867 * @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)
40868 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40870 setUrl : function(url, params, loadOnce){
40872 this.iframeEl.dom.src = url;
40876 if(this.refreshDelegate){
40877 this.removeListener("activate", this.refreshDelegate);
40879 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40880 this.on("activate", this.refreshDelegate);
40881 return this.el.getUpdateManager();
40884 _handleRefresh : function(url, params, loadOnce){
40885 if(!loadOnce || !this.loaded){
40886 var updater = this.el.getUpdateManager();
40887 updater.update(url, params, this._setLoaded.createDelegate(this));
40891 _setLoaded : function(){
40892 this.loaded = true;
40896 * Returns this panel's id
40899 getId : function(){
40904 * Returns this panel's element - used by regiosn to add.
40905 * @return {Roo.Element}
40907 getEl : function(){
40908 return this.wrapEl || this.el;
40913 adjustForComponents : function(width, height)
40915 //Roo.log('adjustForComponents ');
40916 if(this.resizeEl != this.el){
40917 width -= this.el.getFrameWidth('lr');
40918 height -= this.el.getFrameWidth('tb');
40921 var te = this.toolbar.getEl();
40922 te.setWidth(width);
40923 height -= te.getHeight();
40926 var te = this.footer.getEl();
40927 te.setWidth(width);
40928 height -= te.getHeight();
40932 if(this.adjustments){
40933 width += this.adjustments[0];
40934 height += this.adjustments[1];
40936 return {"width": width, "height": height};
40939 setSize : function(width, height){
40940 if(this.fitToFrame && !this.ignoreResize(width, height)){
40941 if(this.fitContainer && this.resizeEl != this.el){
40942 this.el.setSize(width, height);
40944 var size = this.adjustForComponents(width, height);
40946 this.iframeEl.setSize(width,height);
40949 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40950 this.fireEvent('resize', this, size.width, size.height);
40957 * Returns this panel's title
40960 getTitle : function(){
40962 if (typeof(this.title) != 'object') {
40967 for (var k in this.title) {
40968 if (!this.title.hasOwnProperty(k)) {
40972 if (k.indexOf('-') >= 0) {
40973 var s = k.split('-');
40974 for (var i = 0; i<s.length; i++) {
40975 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40978 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40985 * Set this panel's title
40986 * @param {String} title
40988 setTitle : function(title){
40989 this.title = title;
40991 this.region.updatePanelTitle(this, title);
40996 * Returns true is this panel was configured to be closable
40997 * @return {Boolean}
40999 isClosable : function(){
41000 return this.closable;
41003 beforeSlide : function(){
41005 this.resizeEl.clip();
41008 afterSlide : function(){
41010 this.resizeEl.unclip();
41014 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41015 * Will fail silently if the {@link #setUrl} method has not been called.
41016 * This does not activate the panel, just updates its content.
41018 refresh : function(){
41019 if(this.refreshDelegate){
41020 this.loaded = false;
41021 this.refreshDelegate();
41026 * Destroys this panel
41028 destroy : function(){
41029 this.el.removeAllListeners();
41030 var tempEl = document.createElement("span");
41031 tempEl.appendChild(this.el.dom);
41032 tempEl.innerHTML = "";
41038 * form - if the content panel contains a form - this is a reference to it.
41039 * @type {Roo.form.Form}
41043 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41044 * This contains a reference to it.
41050 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41060 * @param {Object} cfg Xtype definition of item to add.
41064 getChildContainer: function () {
41065 return this.getEl();
41069 onScroll : function(e)
41071 this.fireEvent('scroll', this, e);
41076 var ret = new Roo.factory(cfg);
41081 if (cfg.xtype.match(/^Form$/)) {
41084 //if (this.footer) {
41085 // el = this.footer.container.insertSibling(false, 'before');
41087 el = this.el.createChild();
41090 this.form = new Roo.form.Form(cfg);
41093 if ( this.form.allItems.length) {
41094 this.form.render(el.dom);
41098 // should only have one of theses..
41099 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41100 // views.. should not be just added - used named prop 'view''
41102 cfg.el = this.el.appendChild(document.createElement("div"));
41105 var ret = new Roo.factory(cfg);
41107 ret.render && ret.render(false, ''); // render blank..
41117 * @class Roo.bootstrap.panel.Grid
41118 * @extends Roo.bootstrap.panel.Content
41120 * Create a new GridPanel.
41121 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41122 * @param {Object} config A the config object
41128 Roo.bootstrap.panel.Grid = function(config)
41132 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41133 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41135 config.el = this.wrapper;
41136 //this.el = this.wrapper;
41138 if (config.container) {
41139 // ctor'ed from a Border/panel.grid
41142 this.wrapper.setStyle("overflow", "hidden");
41143 this.wrapper.addClass('roo-grid-container');
41148 if(config.toolbar){
41149 var tool_el = this.wrapper.createChild();
41150 this.toolbar = Roo.factory(config.toolbar);
41152 if (config.toolbar.items) {
41153 ti = config.toolbar.items ;
41154 delete config.toolbar.items ;
41158 this.toolbar.render(tool_el);
41159 for(var i =0;i < ti.length;i++) {
41160 // Roo.log(['add child', items[i]]);
41161 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41163 this.toolbar.items = nitems;
41165 delete config.toolbar;
41168 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41169 config.grid.scrollBody = true;;
41170 config.grid.monitorWindowResize = false; // turn off autosizing
41171 config.grid.autoHeight = false;
41172 config.grid.autoWidth = false;
41174 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41176 if (config.background) {
41177 // render grid on panel activation (if panel background)
41178 this.on('activate', function(gp) {
41179 if (!gp.grid.rendered) {
41180 gp.grid.render(this.wrapper);
41181 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41186 this.grid.render(this.wrapper);
41187 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41190 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41191 // ??? needed ??? config.el = this.wrapper;
41196 // xtype created footer. - not sure if will work as we normally have to render first..
41197 if (this.footer && !this.footer.el && this.footer.xtype) {
41199 var ctr = this.grid.getView().getFooterPanel(true);
41200 this.footer.dataSource = this.grid.dataSource;
41201 this.footer = Roo.factory(this.footer, Roo);
41202 this.footer.render(ctr);
41212 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41213 getId : function(){
41214 return this.grid.id;
41218 * Returns the grid for this panel
41219 * @return {Roo.bootstrap.Table}
41221 getGrid : function(){
41225 setSize : function(width, height){
41226 if(!this.ignoreResize(width, height)){
41227 var grid = this.grid;
41228 var size = this.adjustForComponents(width, height);
41229 // tfoot is not a footer?
41232 var gridel = grid.getGridEl();
41233 gridel.setSize(size.width, size.height);
41235 var tbd = grid.getGridEl().select('tbody', true).first();
41236 var thd = grid.getGridEl().select('thead',true).first();
41237 var tbf= grid.getGridEl().select('tfoot', true).first();
41240 size.height -= tbf.getHeight();
41243 size.height -= thd.getHeight();
41246 tbd.setSize(size.width, size.height );
41247 // this is for the account management tab -seems to work there.
41248 var thd = grid.getGridEl().select('thead',true).first();
41250 // tbd.setSize(size.width, size.height - thd.getHeight());
41259 beforeSlide : function(){
41260 this.grid.getView().scroller.clip();
41263 afterSlide : function(){
41264 this.grid.getView().scroller.unclip();
41267 destroy : function(){
41268 this.grid.destroy();
41270 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41275 * @class Roo.bootstrap.panel.Nest
41276 * @extends Roo.bootstrap.panel.Content
41278 * Create a new Panel, that can contain a layout.Border.
41281 * @param {Roo.BorderLayout} layout The layout for this panel
41282 * @param {String/Object} config A string to set only the title or a config object
41284 Roo.bootstrap.panel.Nest = function(config)
41286 // construct with only one argument..
41287 /* FIXME - implement nicer consturctors
41288 if (layout.layout) {
41290 layout = config.layout;
41291 delete config.layout;
41293 if (layout.xtype && !layout.getEl) {
41294 // then layout needs constructing..
41295 layout = Roo.factory(layout, Roo);
41299 config.el = config.layout.getEl();
41301 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41303 config.layout.monitorWindowResize = false; // turn off autosizing
41304 this.layout = config.layout;
41305 this.layout.getEl().addClass("roo-layout-nested-layout");
41306 this.layout.parent = this;
41313 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41315 setSize : function(width, height){
41316 if(!this.ignoreResize(width, height)){
41317 var size = this.adjustForComponents(width, height);
41318 var el = this.layout.getEl();
41319 if (size.height < 1) {
41320 el.setWidth(size.width);
41322 el.setSize(size.width, size.height);
41324 var touch = el.dom.offsetWidth;
41325 this.layout.layout();
41326 // ie requires a double layout on the first pass
41327 if(Roo.isIE && !this.initialized){
41328 this.initialized = true;
41329 this.layout.layout();
41334 // activate all subpanels if not currently active..
41336 setActiveState : function(active){
41337 this.active = active;
41338 this.setActiveClass(active);
41341 this.fireEvent("deactivate", this);
41345 this.fireEvent("activate", this);
41346 // not sure if this should happen before or after..
41347 if (!this.layout) {
41348 return; // should not happen..
41351 for (var r in this.layout.regions) {
41352 reg = this.layout.getRegion(r);
41353 if (reg.getActivePanel()) {
41354 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41355 reg.setActivePanel(reg.getActivePanel());
41358 if (!reg.panels.length) {
41361 reg.showPanel(reg.getPanel(0));
41370 * Returns the nested BorderLayout for this panel
41371 * @return {Roo.BorderLayout}
41373 getLayout : function(){
41374 return this.layout;
41378 * Adds a xtype elements to the layout of the nested panel
41382 xtype : 'ContentPanel',
41389 xtype : 'NestedLayoutPanel',
41395 items : [ ... list of content panels or nested layout panels.. ]
41399 * @param {Object} cfg Xtype definition of item to add.
41401 addxtype : function(cfg) {
41402 return this.layout.addxtype(cfg);
41407 * Ext JS Library 1.1.1
41408 * Copyright(c) 2006-2007, Ext JS, LLC.
41410 * Originally Released Under LGPL - original licence link has changed is not relivant.
41413 * <script type="text/javascript">
41416 * @class Roo.TabPanel
41417 * @extends Roo.util.Observable
41418 * A lightweight tab container.
41422 // basic tabs 1, built from existing content
41423 var tabs = new Roo.TabPanel("tabs1");
41424 tabs.addTab("script", "View Script");
41425 tabs.addTab("markup", "View Markup");
41426 tabs.activate("script");
41428 // more advanced tabs, built from javascript
41429 var jtabs = new Roo.TabPanel("jtabs");
41430 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41432 // set up the UpdateManager
41433 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41434 var updater = tab2.getUpdateManager();
41435 updater.setDefaultUrl("ajax1.htm");
41436 tab2.on('activate', updater.refresh, updater, true);
41438 // Use setUrl for Ajax loading
41439 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41440 tab3.setUrl("ajax2.htm", null, true);
41443 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41446 jtabs.activate("jtabs-1");
41449 * Create a new TabPanel.
41450 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41451 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41453 Roo.bootstrap.panel.Tabs = function(config){
41455 * The container element for this TabPanel.
41456 * @type Roo.Element
41458 this.el = Roo.get(config.el);
41461 if(typeof config == "boolean"){
41462 this.tabPosition = config ? "bottom" : "top";
41464 Roo.apply(this, config);
41468 if(this.tabPosition == "bottom"){
41469 // if tabs are at the bottom = create the body first.
41470 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41471 this.el.addClass("roo-tabs-bottom");
41473 // next create the tabs holders
41475 if (this.tabPosition == "west"){
41477 var reg = this.region; // fake it..
41479 if (!reg.mgr.parent) {
41482 reg = reg.mgr.parent.region;
41484 Roo.log("got nest?");
41486 if (reg.mgr.getRegion('west')) {
41487 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41488 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41489 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41490 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41491 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41499 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41500 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41501 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41502 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41507 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41510 // finally - if tabs are at the top, then create the body last..
41511 if(this.tabPosition != "bottom"){
41512 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41513 * @type Roo.Element
41515 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41516 this.el.addClass("roo-tabs-top");
41520 this.bodyEl.setStyle("position", "relative");
41522 this.active = null;
41523 this.activateDelegate = this.activate.createDelegate(this);
41528 * Fires when the active tab changes
41529 * @param {Roo.TabPanel} this
41530 * @param {Roo.TabPanelItem} activePanel The new active tab
41534 * @event beforetabchange
41535 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41536 * @param {Roo.TabPanel} this
41537 * @param {Object} e Set cancel to true on this object to cancel the tab change
41538 * @param {Roo.TabPanelItem} tab The tab being changed to
41540 "beforetabchange" : true
41543 Roo.EventManager.onWindowResize(this.onResize, this);
41544 this.cpad = this.el.getPadding("lr");
41545 this.hiddenCount = 0;
41548 // toolbar on the tabbar support...
41549 if (this.toolbar) {
41550 alert("no toolbar support yet");
41551 this.toolbar = false;
41553 var tcfg = this.toolbar;
41554 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41555 this.toolbar = new Roo.Toolbar(tcfg);
41556 if (Roo.isSafari) {
41557 var tbl = tcfg.container.child('table', true);
41558 tbl.setAttribute('width', '100%');
41566 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41569 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41571 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41573 tabPosition : "top",
41575 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41577 currentTabWidth : 0,
41579 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41583 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41587 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41589 preferredTabWidth : 175,
41591 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41593 resizeTabs : false,
41595 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41597 monitorResize : true,
41599 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41601 toolbar : false, // set by caller..
41603 region : false, /// set by caller
41605 disableTooltips : true, // not used yet...
41608 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41609 * @param {String} id The id of the div to use <b>or create</b>
41610 * @param {String} text The text for the tab
41611 * @param {String} content (optional) Content to put in the TabPanelItem body
41612 * @param {Boolean} closable (optional) True to create a close icon on the tab
41613 * @return {Roo.TabPanelItem} The created TabPanelItem
41615 addTab : function(id, text, content, closable, tpl)
41617 var item = new Roo.bootstrap.panel.TabItem({
41621 closable : closable,
41624 this.addTabItem(item);
41626 item.setContent(content);
41632 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41633 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41634 * @return {Roo.TabPanelItem}
41636 getTab : function(id){
41637 return this.items[id];
41641 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41642 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41644 hideTab : function(id){
41645 var t = this.items[id];
41648 this.hiddenCount++;
41649 this.autoSizeTabs();
41654 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41655 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41657 unhideTab : function(id){
41658 var t = this.items[id];
41660 t.setHidden(false);
41661 this.hiddenCount--;
41662 this.autoSizeTabs();
41667 * Adds an existing {@link Roo.TabPanelItem}.
41668 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41670 addTabItem : function(item)
41672 this.items[item.id] = item;
41673 this.items.push(item);
41674 this.autoSizeTabs();
41675 // if(this.resizeTabs){
41676 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41677 // this.autoSizeTabs();
41679 // item.autoSize();
41684 * Removes a {@link Roo.TabPanelItem}.
41685 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41687 removeTab : function(id){
41688 var items = this.items;
41689 var tab = items[id];
41690 if(!tab) { return; }
41691 var index = items.indexOf(tab);
41692 if(this.active == tab && items.length > 1){
41693 var newTab = this.getNextAvailable(index);
41698 this.stripEl.dom.removeChild(tab.pnode.dom);
41699 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41700 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41702 items.splice(index, 1);
41703 delete this.items[tab.id];
41704 tab.fireEvent("close", tab);
41705 tab.purgeListeners();
41706 this.autoSizeTabs();
41709 getNextAvailable : function(start){
41710 var items = this.items;
41712 // look for a next tab that will slide over to
41713 // replace the one being removed
41714 while(index < items.length){
41715 var item = items[++index];
41716 if(item && !item.isHidden()){
41720 // if one isn't found select the previous tab (on the left)
41723 var item = items[--index];
41724 if(item && !item.isHidden()){
41732 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41733 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41735 disableTab : function(id){
41736 var tab = this.items[id];
41737 if(tab && this.active != tab){
41743 * Enables a {@link Roo.TabPanelItem} that is disabled.
41744 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41746 enableTab : function(id){
41747 var tab = this.items[id];
41752 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41753 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41754 * @return {Roo.TabPanelItem} The TabPanelItem.
41756 activate : function(id)
41758 //Roo.log('activite:' + id);
41760 var tab = this.items[id];
41764 if(tab == this.active || tab.disabled){
41768 this.fireEvent("beforetabchange", this, e, tab);
41769 if(e.cancel !== true && !tab.disabled){
41771 this.active.hide();
41773 this.active = this.items[id];
41774 this.active.show();
41775 this.fireEvent("tabchange", this, this.active);
41781 * Gets the active {@link Roo.TabPanelItem}.
41782 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41784 getActiveTab : function(){
41785 return this.active;
41789 * Updates the tab body element to fit the height of the container element
41790 * for overflow scrolling
41791 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41793 syncHeight : function(targetHeight){
41794 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41795 var bm = this.bodyEl.getMargins();
41796 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41797 this.bodyEl.setHeight(newHeight);
41801 onResize : function(){
41802 if(this.monitorResize){
41803 this.autoSizeTabs();
41808 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41810 beginUpdate : function(){
41811 this.updating = true;
41815 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41817 endUpdate : function(){
41818 this.updating = false;
41819 this.autoSizeTabs();
41823 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41825 autoSizeTabs : function()
41827 var count = this.items.length;
41828 var vcount = count - this.hiddenCount;
41831 this.stripEl.hide();
41833 this.stripEl.show();
41836 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41841 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41842 var availWidth = Math.floor(w / vcount);
41843 var b = this.stripBody;
41844 if(b.getWidth() > w){
41845 var tabs = this.items;
41846 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41847 if(availWidth < this.minTabWidth){
41848 /*if(!this.sleft){ // incomplete scrolling code
41849 this.createScrollButtons();
41852 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41855 if(this.currentTabWidth < this.preferredTabWidth){
41856 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41862 * Returns the number of tabs in this TabPanel.
41865 getCount : function(){
41866 return this.items.length;
41870 * Resizes all the tabs to the passed width
41871 * @param {Number} The new width
41873 setTabWidth : function(width){
41874 this.currentTabWidth = width;
41875 for(var i = 0, len = this.items.length; i < len; i++) {
41876 if(!this.items[i].isHidden()) {
41877 this.items[i].setWidth(width);
41883 * Destroys this TabPanel
41884 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41886 destroy : function(removeEl){
41887 Roo.EventManager.removeResizeListener(this.onResize, this);
41888 for(var i = 0, len = this.items.length; i < len; i++){
41889 this.items[i].purgeListeners();
41891 if(removeEl === true){
41892 this.el.update("");
41897 createStrip : function(container)
41899 var strip = document.createElement("nav");
41900 strip.className = Roo.bootstrap.version == 4 ?
41901 "navbar-light bg-light" :
41902 "navbar navbar-default"; //"x-tabs-wrap";
41903 container.appendChild(strip);
41907 createStripList : function(strip)
41909 // div wrapper for retard IE
41910 // returns the "tr" element.
41911 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41912 //'<div class="x-tabs-strip-wrap">'+
41913 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41914 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41915 return strip.firstChild; //.firstChild.firstChild.firstChild;
41917 createBody : function(container)
41919 var body = document.createElement("div");
41920 Roo.id(body, "tab-body");
41921 //Roo.fly(body).addClass("x-tabs-body");
41922 Roo.fly(body).addClass("tab-content");
41923 container.appendChild(body);
41926 createItemBody :function(bodyEl, id){
41927 var body = Roo.getDom(id);
41929 body = document.createElement("div");
41932 //Roo.fly(body).addClass("x-tabs-item-body");
41933 Roo.fly(body).addClass("tab-pane");
41934 bodyEl.insertBefore(body, bodyEl.firstChild);
41938 createStripElements : function(stripEl, text, closable, tpl)
41940 var td = document.createElement("li"); // was td..
41941 td.className = 'nav-item';
41943 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41946 stripEl.appendChild(td);
41948 td.className = "x-tabs-closable";
41949 if(!this.closeTpl){
41950 this.closeTpl = new Roo.Template(
41951 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41952 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41953 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41956 var el = this.closeTpl.overwrite(td, {"text": text});
41957 var close = el.getElementsByTagName("div")[0];
41958 var inner = el.getElementsByTagName("em")[0];
41959 return {"el": el, "close": close, "inner": inner};
41962 // not sure what this is..
41963 // if(!this.tabTpl){
41964 //this.tabTpl = new Roo.Template(
41965 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41966 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41968 // this.tabTpl = new Roo.Template(
41969 // '<a href="#">' +
41970 // '<span unselectable="on"' +
41971 // (this.disableTooltips ? '' : ' title="{text}"') +
41972 // ' >{text}</span></a>'
41978 var template = tpl || this.tabTpl || false;
41981 template = new Roo.Template(
41982 Roo.bootstrap.version == 4 ?
41984 '<a class="nav-link" href="#" unselectable="on"' +
41985 (this.disableTooltips ? '' : ' title="{text}"') +
41988 '<a class="nav-link" href="#">' +
41989 '<span unselectable="on"' +
41990 (this.disableTooltips ? '' : ' title="{text}"') +
41991 ' >{text}</span></a>'
41996 switch (typeof(template)) {
42000 template = new Roo.Template(template);
42006 var el = template.overwrite(td, {"text": text});
42008 var inner = el.getElementsByTagName("span")[0];
42010 return {"el": el, "inner": inner};
42018 * @class Roo.TabPanelItem
42019 * @extends Roo.util.Observable
42020 * Represents an individual item (tab plus body) in a TabPanel.
42021 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42022 * @param {String} id The id of this TabPanelItem
42023 * @param {String} text The text for the tab of this TabPanelItem
42024 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42026 Roo.bootstrap.panel.TabItem = function(config){
42028 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42029 * @type Roo.TabPanel
42031 this.tabPanel = config.panel;
42033 * The id for this TabPanelItem
42036 this.id = config.id;
42038 this.disabled = false;
42040 this.text = config.text;
42042 this.loaded = false;
42043 this.closable = config.closable;
42046 * The body element for this TabPanelItem.
42047 * @type Roo.Element
42049 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42050 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42051 this.bodyEl.setStyle("display", "block");
42052 this.bodyEl.setStyle("zoom", "1");
42053 //this.hideAction();
42055 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42057 this.el = Roo.get(els.el);
42058 this.inner = Roo.get(els.inner, true);
42059 this.textEl = Roo.bootstrap.version == 4 ?
42060 this.el : Roo.get(this.el.dom.firstChild, true);
42062 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42063 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42066 // this.el.on("mousedown", this.onTabMouseDown, this);
42067 this.el.on("click", this.onTabClick, this);
42069 if(config.closable){
42070 var c = Roo.get(els.close, true);
42071 c.dom.title = this.closeText;
42072 c.addClassOnOver("close-over");
42073 c.on("click", this.closeClick, this);
42079 * Fires when this tab becomes the active tab.
42080 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42081 * @param {Roo.TabPanelItem} this
42085 * @event beforeclose
42086 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42087 * @param {Roo.TabPanelItem} this
42088 * @param {Object} e Set cancel to true on this object to cancel the close.
42090 "beforeclose": true,
42093 * Fires when this tab is closed.
42094 * @param {Roo.TabPanelItem} this
42098 * @event deactivate
42099 * Fires when this tab is no longer the active tab.
42100 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42101 * @param {Roo.TabPanelItem} this
42103 "deactivate" : true
42105 this.hidden = false;
42107 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42110 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42112 purgeListeners : function(){
42113 Roo.util.Observable.prototype.purgeListeners.call(this);
42114 this.el.removeAllListeners();
42117 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42120 this.status_node.addClass("active");
42123 this.tabPanel.stripWrap.repaint();
42125 this.fireEvent("activate", this.tabPanel, this);
42129 * Returns true if this tab is the active tab.
42130 * @return {Boolean}
42132 isActive : function(){
42133 return this.tabPanel.getActiveTab() == this;
42137 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42140 this.status_node.removeClass("active");
42142 this.fireEvent("deactivate", this.tabPanel, this);
42145 hideAction : function(){
42146 this.bodyEl.hide();
42147 this.bodyEl.setStyle("position", "absolute");
42148 this.bodyEl.setLeft("-20000px");
42149 this.bodyEl.setTop("-20000px");
42152 showAction : function(){
42153 this.bodyEl.setStyle("position", "relative");
42154 this.bodyEl.setTop("");
42155 this.bodyEl.setLeft("");
42156 this.bodyEl.show();
42160 * Set the tooltip for the tab.
42161 * @param {String} tooltip The tab's tooltip
42163 setTooltip : function(text){
42164 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42165 this.textEl.dom.qtip = text;
42166 this.textEl.dom.removeAttribute('title');
42168 this.textEl.dom.title = text;
42172 onTabClick : function(e){
42173 e.preventDefault();
42174 this.tabPanel.activate(this.id);
42177 onTabMouseDown : function(e){
42178 e.preventDefault();
42179 this.tabPanel.activate(this.id);
42182 getWidth : function(){
42183 return this.inner.getWidth();
42186 setWidth : function(width){
42187 var iwidth = width - this.linode.getPadding("lr");
42188 this.inner.setWidth(iwidth);
42189 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42190 this.linode.setWidth(width);
42194 * Show or hide the tab
42195 * @param {Boolean} hidden True to hide or false to show.
42197 setHidden : function(hidden){
42198 this.hidden = hidden;
42199 this.linode.setStyle("display", hidden ? "none" : "");
42203 * Returns true if this tab is "hidden"
42204 * @return {Boolean}
42206 isHidden : function(){
42207 return this.hidden;
42211 * Returns the text for this tab
42214 getText : function(){
42218 autoSize : function(){
42219 //this.el.beginMeasure();
42220 this.textEl.setWidth(1);
42222 * #2804 [new] Tabs in Roojs
42223 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42225 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42226 //this.el.endMeasure();
42230 * Sets the text for the tab (Note: this also sets the tooltip text)
42231 * @param {String} text The tab's text and tooltip
42233 setText : function(text){
42235 this.textEl.update(text);
42236 this.setTooltip(text);
42237 //if(!this.tabPanel.resizeTabs){
42238 // this.autoSize();
42242 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42244 activate : function(){
42245 this.tabPanel.activate(this.id);
42249 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42251 disable : function(){
42252 if(this.tabPanel.active != this){
42253 this.disabled = true;
42254 this.status_node.addClass("disabled");
42259 * Enables this TabPanelItem if it was previously disabled.
42261 enable : function(){
42262 this.disabled = false;
42263 this.status_node.removeClass("disabled");
42267 * Sets the content for this TabPanelItem.
42268 * @param {String} content The content
42269 * @param {Boolean} loadScripts true to look for and load scripts
42271 setContent : function(content, loadScripts){
42272 this.bodyEl.update(content, loadScripts);
42276 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42277 * @return {Roo.UpdateManager} The UpdateManager
42279 getUpdateManager : function(){
42280 return this.bodyEl.getUpdateManager();
42284 * Set a URL to be used to load the content for this TabPanelItem.
42285 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42286 * @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)
42287 * @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)
42288 * @return {Roo.UpdateManager} The UpdateManager
42290 setUrl : function(url, params, loadOnce){
42291 if(this.refreshDelegate){
42292 this.un('activate', this.refreshDelegate);
42294 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42295 this.on("activate", this.refreshDelegate);
42296 return this.bodyEl.getUpdateManager();
42300 _handleRefresh : function(url, params, loadOnce){
42301 if(!loadOnce || !this.loaded){
42302 var updater = this.bodyEl.getUpdateManager();
42303 updater.update(url, params, this._setLoaded.createDelegate(this));
42308 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42309 * Will fail silently if the setUrl method has not been called.
42310 * This does not activate the panel, just updates its content.
42312 refresh : function(){
42313 if(this.refreshDelegate){
42314 this.loaded = false;
42315 this.refreshDelegate();
42320 _setLoaded : function(){
42321 this.loaded = true;
42325 closeClick : function(e){
42328 this.fireEvent("beforeclose", this, o);
42329 if(o.cancel !== true){
42330 this.tabPanel.removeTab(this.id);
42334 * The text displayed in the tooltip for the close icon.
42337 closeText : "Close this tab"
42340 * This script refer to:
42341 * Title: International Telephone Input
42342 * Author: Jack O'Connor
42343 * Code version: v12.1.12
42344 * Availability: https://github.com/jackocnr/intl-tel-input.git
42347 Roo.bootstrap.PhoneInputData = function() {
42350 "Afghanistan (افغانستان)",
42355 "Albania (Shqipëri)",
42360 "Algeria (الجزائر)",
42385 "Antigua and Barbuda",
42395 "Armenia (Հայաստան)",
42411 "Austria (Österreich)",
42416 "Azerbaijan (Azərbaycan)",
42426 "Bahrain (البحرين)",
42431 "Bangladesh (বাংলাদেশ)",
42441 "Belarus (Беларусь)",
42446 "Belgium (België)",
42476 "Bosnia and Herzegovina (Босна и Херцеговина)",
42491 "British Indian Ocean Territory",
42496 "British Virgin Islands",
42506 "Bulgaria (България)",
42516 "Burundi (Uburundi)",
42521 "Cambodia (កម្ពុជា)",
42526 "Cameroon (Cameroun)",
42535 ["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"]
42538 "Cape Verde (Kabu Verdi)",
42543 "Caribbean Netherlands",
42554 "Central African Republic (République centrafricaine)",
42574 "Christmas Island",
42580 "Cocos (Keeling) Islands",
42591 "Comoros (جزر القمر)",
42596 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42601 "Congo (Republic) (Congo-Brazzaville)",
42621 "Croatia (Hrvatska)",
42642 "Czech Republic (Česká republika)",
42647 "Denmark (Danmark)",
42662 "Dominican Republic (República Dominicana)",
42666 ["809", "829", "849"]
42684 "Equatorial Guinea (Guinea Ecuatorial)",
42704 "Falkland Islands (Islas Malvinas)",
42709 "Faroe Islands (Føroyar)",
42730 "French Guiana (Guyane française)",
42735 "French Polynesia (Polynésie française)",
42750 "Georgia (საქართველო)",
42755 "Germany (Deutschland)",
42775 "Greenland (Kalaallit Nunaat)",
42812 "Guinea-Bissau (Guiné Bissau)",
42837 "Hungary (Magyarország)",
42842 "Iceland (Ísland)",
42862 "Iraq (العراق)",
42878 "Israel (ישראל)",
42905 "Jordan (الأردن)",
42910 "Kazakhstan (Казахстан)",
42931 "Kuwait (الكويت)",
42936 "Kyrgyzstan (Кыргызстан)",
42946 "Latvia (Latvija)",
42951 "Lebanon (لبنان)",
42966 "Libya (ليبيا)",
42976 "Lithuania (Lietuva)",
42991 "Macedonia (FYROM) (Македонија)",
42996 "Madagascar (Madagasikara)",
43026 "Marshall Islands",
43036 "Mauritania (موريتانيا)",
43041 "Mauritius (Moris)",
43062 "Moldova (Republica Moldova)",
43072 "Mongolia (Монгол)",
43077 "Montenegro (Crna Gora)",
43087 "Morocco (المغرب)",
43093 "Mozambique (Moçambique)",
43098 "Myanmar (Burma) (မြန်မာ)",
43103 "Namibia (Namibië)",
43118 "Netherlands (Nederland)",
43123 "New Caledonia (Nouvelle-Calédonie)",
43158 "North Korea (조선 민주주의 인민 공화국)",
43163 "Northern Mariana Islands",
43179 "Pakistan (پاکستان)",
43189 "Palestine (فلسطين)",
43199 "Papua New Guinea",
43241 "Réunion (La Réunion)",
43247 "Romania (România)",
43263 "Saint Barthélemy",
43274 "Saint Kitts and Nevis",
43284 "Saint Martin (Saint-Martin (partie française))",
43290 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43295 "Saint Vincent and the Grenadines",
43310 "São Tomé and Príncipe (São Tomé e Príncipe)",
43315 "Saudi Arabia (المملكة العربية السعودية)",
43320 "Senegal (Sénégal)",
43350 "Slovakia (Slovensko)",
43355 "Slovenia (Slovenija)",
43365 "Somalia (Soomaaliya)",
43375 "South Korea (대한민국)",
43380 "South Sudan (جنوب السودان)",
43390 "Sri Lanka (ශ්රී ලංකාව)",
43395 "Sudan (السودان)",
43405 "Svalbard and Jan Mayen",
43416 "Sweden (Sverige)",
43421 "Switzerland (Schweiz)",
43426 "Syria (سوريا)",
43471 "Trinidad and Tobago",
43476 "Tunisia (تونس)",
43481 "Turkey (Türkiye)",
43491 "Turks and Caicos Islands",
43501 "U.S. Virgin Islands",
43511 "Ukraine (Україна)",
43516 "United Arab Emirates (الإمارات العربية المتحدة)",
43538 "Uzbekistan (Oʻzbekiston)",
43548 "Vatican City (Città del Vaticano)",
43559 "Vietnam (Việt Nam)",
43564 "Wallis and Futuna (Wallis-et-Futuna)",
43569 "Western Sahara (الصحراء الغربية)",
43575 "Yemen (اليمن)",
43599 * This script refer to:
43600 * Title: International Telephone Input
43601 * Author: Jack O'Connor
43602 * Code version: v12.1.12
43603 * Availability: https://github.com/jackocnr/intl-tel-input.git
43607 * @class Roo.bootstrap.PhoneInput
43608 * @extends Roo.bootstrap.TriggerField
43609 * An input with International dial-code selection
43611 * @cfg {String} defaultDialCode default '+852'
43612 * @cfg {Array} preferedCountries default []
43615 * Create a new PhoneInput.
43616 * @param {Object} config Configuration options
43619 Roo.bootstrap.PhoneInput = function(config) {
43620 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43623 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43625 listWidth: undefined,
43627 selectedClass: 'active',
43629 invalidClass : "has-warning",
43631 validClass: 'has-success',
43633 allowed: '0123456789',
43638 * @cfg {String} defaultDialCode The default dial code when initializing the input
43640 defaultDialCode: '+852',
43643 * @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
43645 preferedCountries: false,
43647 getAutoCreate : function()
43649 var data = Roo.bootstrap.PhoneInputData();
43650 var align = this.labelAlign || this.parentLabelAlign();
43653 this.allCountries = [];
43654 this.dialCodeMapping = [];
43656 for (var i = 0; i < data.length; i++) {
43658 this.allCountries[i] = {
43662 priority: c[3] || 0,
43663 areaCodes: c[4] || null
43665 this.dialCodeMapping[c[2]] = {
43668 priority: c[3] || 0,
43669 areaCodes: c[4] || null
43681 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43682 maxlength: this.max_length,
43683 cls : 'form-control tel-input',
43684 autocomplete: 'new-password'
43687 var hiddenInput = {
43690 cls: 'hidden-tel-input'
43694 hiddenInput.name = this.name;
43697 if (this.disabled) {
43698 input.disabled = true;
43701 var flag_container = {
43718 cls: this.hasFeedback ? 'has-feedback' : '',
43724 cls: 'dial-code-holder',
43731 cls: 'roo-select2-container input-group',
43738 if (this.fieldLabel.length) {
43741 tooltip: 'This field is required'
43747 cls: 'control-label',
43753 html: this.fieldLabel
43756 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43762 if(this.indicatorpos == 'right') {
43763 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43770 if(align == 'left') {
43778 if(this.labelWidth > 12){
43779 label.style = "width: " + this.labelWidth + 'px';
43781 if(this.labelWidth < 13 && this.labelmd == 0){
43782 this.labelmd = this.labelWidth;
43784 if(this.labellg > 0){
43785 label.cls += ' col-lg-' + this.labellg;
43786 input.cls += ' col-lg-' + (12 - this.labellg);
43788 if(this.labelmd > 0){
43789 label.cls += ' col-md-' + this.labelmd;
43790 container.cls += ' col-md-' + (12 - this.labelmd);
43792 if(this.labelsm > 0){
43793 label.cls += ' col-sm-' + this.labelsm;
43794 container.cls += ' col-sm-' + (12 - this.labelsm);
43796 if(this.labelxs > 0){
43797 label.cls += ' col-xs-' + this.labelxs;
43798 container.cls += ' col-xs-' + (12 - this.labelxs);
43808 var settings = this;
43810 ['xs','sm','md','lg'].map(function(size){
43811 if (settings[size]) {
43812 cfg.cls += ' col-' + size + '-' + settings[size];
43816 this.store = new Roo.data.Store({
43817 proxy : new Roo.data.MemoryProxy({}),
43818 reader : new Roo.data.JsonReader({
43829 'name' : 'dialCode',
43833 'name' : 'priority',
43837 'name' : 'areaCodes',
43844 if(!this.preferedCountries) {
43845 this.preferedCountries = [
43852 var p = this.preferedCountries.reverse();
43855 for (var i = 0; i < p.length; i++) {
43856 for (var j = 0; j < this.allCountries.length; j++) {
43857 if(this.allCountries[j].iso2 == p[i]) {
43858 var t = this.allCountries[j];
43859 this.allCountries.splice(j,1);
43860 this.allCountries.unshift(t);
43866 this.store.proxy.data = {
43868 data: this.allCountries
43874 initEvents : function()
43877 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43879 this.indicator = this.indicatorEl();
43880 this.flag = this.flagEl();
43881 this.dialCodeHolder = this.dialCodeHolderEl();
43883 this.trigger = this.el.select('div.flag-box',true).first();
43884 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43889 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43890 _this.list.setWidth(lw);
43893 this.list.on('mouseover', this.onViewOver, this);
43894 this.list.on('mousemove', this.onViewMove, this);
43895 this.inputEl().on("keyup", this.onKeyUp, this);
43896 this.inputEl().on("keypress", this.onKeyPress, this);
43898 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43900 this.view = new Roo.View(this.list, this.tpl, {
43901 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43904 this.view.on('click', this.onViewClick, this);
43905 this.setValue(this.defaultDialCode);
43908 onTriggerClick : function(e)
43910 Roo.log('trigger click');
43915 if(this.isExpanded()){
43917 this.hasFocus = false;
43919 this.store.load({});
43920 this.hasFocus = true;
43925 isExpanded : function()
43927 return this.list.isVisible();
43930 collapse : function()
43932 if(!this.isExpanded()){
43936 Roo.get(document).un('mousedown', this.collapseIf, this);
43937 Roo.get(document).un('mousewheel', this.collapseIf, this);
43938 this.fireEvent('collapse', this);
43942 expand : function()
43946 if(this.isExpanded() || !this.hasFocus){
43950 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43951 this.list.setWidth(lw);
43954 this.restrictHeight();
43956 Roo.get(document).on('mousedown', this.collapseIf, this);
43957 Roo.get(document).on('mousewheel', this.collapseIf, this);
43959 this.fireEvent('expand', this);
43962 restrictHeight : function()
43964 this.list.alignTo(this.inputEl(), this.listAlign);
43965 this.list.alignTo(this.inputEl(), this.listAlign);
43968 onViewOver : function(e, t)
43970 if(this.inKeyMode){
43973 var item = this.view.findItemFromChild(t);
43976 var index = this.view.indexOf(item);
43977 this.select(index, false);
43982 onViewClick : function(view, doFocus, el, e)
43984 var index = this.view.getSelectedIndexes()[0];
43986 var r = this.store.getAt(index);
43989 this.onSelect(r, index);
43991 if(doFocus !== false && !this.blockFocus){
43992 this.inputEl().focus();
43996 onViewMove : function(e, t)
43998 this.inKeyMode = false;
44001 select : function(index, scrollIntoView)
44003 this.selectedIndex = index;
44004 this.view.select(index);
44005 if(scrollIntoView !== false){
44006 var el = this.view.getNode(index);
44008 this.list.scrollChildIntoView(el, false);
44013 createList : function()
44015 this.list = Roo.get(document.body).createChild({
44017 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44018 style: 'display:none'
44021 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44024 collapseIf : function(e)
44026 var in_combo = e.within(this.el);
44027 var in_list = e.within(this.list);
44028 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44030 if (in_combo || in_list || is_list) {
44036 onSelect : function(record, index)
44038 if(this.fireEvent('beforeselect', this, record, index) !== false){
44040 this.setFlagClass(record.data.iso2);
44041 this.setDialCode(record.data.dialCode);
44042 this.hasFocus = false;
44044 this.fireEvent('select', this, record, index);
44048 flagEl : function()
44050 var flag = this.el.select('div.flag',true).first();
44057 dialCodeHolderEl : function()
44059 var d = this.el.select('input.dial-code-holder',true).first();
44066 setDialCode : function(v)
44068 this.dialCodeHolder.dom.value = '+'+v;
44071 setFlagClass : function(n)
44073 this.flag.dom.className = 'flag '+n;
44076 getValue : function()
44078 var v = this.inputEl().getValue();
44079 if(this.dialCodeHolder) {
44080 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44085 setValue : function(v)
44087 var d = this.getDialCode(v);
44089 //invalid dial code
44090 if(v.length == 0 || !d || d.length == 0) {
44092 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44093 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44099 this.setFlagClass(this.dialCodeMapping[d].iso2);
44100 this.setDialCode(d);
44101 this.inputEl().dom.value = v.replace('+'+d,'');
44102 this.hiddenEl().dom.value = this.getValue();
44107 getDialCode : function(v)
44111 if (v.length == 0) {
44112 return this.dialCodeHolder.dom.value;
44116 if (v.charAt(0) != "+") {
44119 var numericChars = "";
44120 for (var i = 1; i < v.length; i++) {
44121 var c = v.charAt(i);
44124 if (this.dialCodeMapping[numericChars]) {
44125 dialCode = v.substr(1, i);
44127 if (numericChars.length == 4) {
44137 this.setValue(this.defaultDialCode);
44141 hiddenEl : function()
44143 return this.el.select('input.hidden-tel-input',true).first();
44146 // after setting val
44147 onKeyUp : function(e){
44148 this.setValue(this.getValue());
44151 onKeyPress : function(e){
44152 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44159 * @class Roo.bootstrap.MoneyField
44160 * @extends Roo.bootstrap.ComboBox
44161 * Bootstrap MoneyField class
44164 * Create a new MoneyField.
44165 * @param {Object} config Configuration options
44168 Roo.bootstrap.MoneyField = function(config) {
44170 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44174 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44177 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44179 allowDecimals : true,
44181 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44183 decimalSeparator : ".",
44185 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44187 decimalPrecision : 0,
44189 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44191 allowNegative : true,
44193 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44197 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44199 minValue : Number.NEGATIVE_INFINITY,
44201 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44203 maxValue : Number.MAX_VALUE,
44205 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44207 minText : "The minimum value for this field is {0}",
44209 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44211 maxText : "The maximum value for this field is {0}",
44213 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44214 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44216 nanText : "{0} is not a valid number",
44218 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44222 * @cfg {String} defaults currency of the MoneyField
44223 * value should be in lkey
44225 defaultCurrency : false,
44227 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44229 thousandsDelimiter : false,
44231 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44242 getAutoCreate : function()
44244 var align = this.labelAlign || this.parentLabelAlign();
44256 cls : 'form-control roo-money-amount-input',
44257 autocomplete: 'new-password'
44260 var hiddenInput = {
44264 cls: 'hidden-number-input'
44267 if(this.max_length) {
44268 input.maxlength = this.max_length;
44272 hiddenInput.name = this.name;
44275 if (this.disabled) {
44276 input.disabled = true;
44279 var clg = 12 - this.inputlg;
44280 var cmd = 12 - this.inputmd;
44281 var csm = 12 - this.inputsm;
44282 var cxs = 12 - this.inputxs;
44286 cls : 'row roo-money-field',
44290 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44294 cls: 'roo-select2-container input-group',
44298 cls : 'form-control roo-money-currency-input',
44299 autocomplete: 'new-password',
44301 name : this.currencyName
44305 cls : 'input-group-addon',
44319 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44323 cls: this.hasFeedback ? 'has-feedback' : '',
44334 if (this.fieldLabel.length) {
44337 tooltip: 'This field is required'
44343 cls: 'control-label',
44349 html: this.fieldLabel
44352 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44358 if(this.indicatorpos == 'right') {
44359 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44366 if(align == 'left') {
44374 if(this.labelWidth > 12){
44375 label.style = "width: " + this.labelWidth + 'px';
44377 if(this.labelWidth < 13 && this.labelmd == 0){
44378 this.labelmd = this.labelWidth;
44380 if(this.labellg > 0){
44381 label.cls += ' col-lg-' + this.labellg;
44382 input.cls += ' col-lg-' + (12 - this.labellg);
44384 if(this.labelmd > 0){
44385 label.cls += ' col-md-' + this.labelmd;
44386 container.cls += ' col-md-' + (12 - this.labelmd);
44388 if(this.labelsm > 0){
44389 label.cls += ' col-sm-' + this.labelsm;
44390 container.cls += ' col-sm-' + (12 - this.labelsm);
44392 if(this.labelxs > 0){
44393 label.cls += ' col-xs-' + this.labelxs;
44394 container.cls += ' col-xs-' + (12 - this.labelxs);
44405 var settings = this;
44407 ['xs','sm','md','lg'].map(function(size){
44408 if (settings[size]) {
44409 cfg.cls += ' col-' + size + '-' + settings[size];
44416 initEvents : function()
44418 this.indicator = this.indicatorEl();
44420 this.initCurrencyEvent();
44422 this.initNumberEvent();
44425 initCurrencyEvent : function()
44428 throw "can not find store for combo";
44431 this.store = Roo.factory(this.store, Roo.data);
44432 this.store.parent = this;
44436 this.triggerEl = this.el.select('.input-group-addon', true).first();
44438 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44443 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44444 _this.list.setWidth(lw);
44447 this.list.on('mouseover', this.onViewOver, this);
44448 this.list.on('mousemove', this.onViewMove, this);
44449 this.list.on('scroll', this.onViewScroll, this);
44452 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44455 this.view = new Roo.View(this.list, this.tpl, {
44456 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44459 this.view.on('click', this.onViewClick, this);
44461 this.store.on('beforeload', this.onBeforeLoad, this);
44462 this.store.on('load', this.onLoad, this);
44463 this.store.on('loadexception', this.onLoadException, this);
44465 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44466 "up" : function(e){
44467 this.inKeyMode = true;
44471 "down" : function(e){
44472 if(!this.isExpanded()){
44473 this.onTriggerClick();
44475 this.inKeyMode = true;
44480 "enter" : function(e){
44483 if(this.fireEvent("specialkey", this, e)){
44484 this.onViewClick(false);
44490 "esc" : function(e){
44494 "tab" : function(e){
44497 if(this.fireEvent("specialkey", this, e)){
44498 this.onViewClick(false);
44506 doRelay : function(foo, bar, hname){
44507 if(hname == 'down' || this.scope.isExpanded()){
44508 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44516 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44520 initNumberEvent : function(e)
44522 this.inputEl().on("keydown" , this.fireKey, this);
44523 this.inputEl().on("focus", this.onFocus, this);
44524 this.inputEl().on("blur", this.onBlur, this);
44526 this.inputEl().relayEvent('keyup', this);
44528 if(this.indicator){
44529 this.indicator.addClass('invisible');
44532 this.originalValue = this.getValue();
44534 if(this.validationEvent == 'keyup'){
44535 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44536 this.inputEl().on('keyup', this.filterValidation, this);
44538 else if(this.validationEvent !== false){
44539 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44542 if(this.selectOnFocus){
44543 this.on("focus", this.preFocus, this);
44546 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44547 this.inputEl().on("keypress", this.filterKeys, this);
44549 this.inputEl().relayEvent('keypress', this);
44552 var allowed = "0123456789";
44554 if(this.allowDecimals){
44555 allowed += this.decimalSeparator;
44558 if(this.allowNegative){
44562 if(this.thousandsDelimiter) {
44566 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44568 var keyPress = function(e){
44570 var k = e.getKey();
44572 var c = e.getCharCode();
44575 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44576 allowed.indexOf(String.fromCharCode(c)) === -1
44582 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44586 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44591 this.inputEl().on("keypress", keyPress, this);
44595 onTriggerClick : function(e)
44602 this.loadNext = false;
44604 if(this.isExpanded()){
44609 this.hasFocus = true;
44611 if(this.triggerAction == 'all') {
44612 this.doQuery(this.allQuery, true);
44616 this.doQuery(this.getRawValue());
44619 getCurrency : function()
44621 var v = this.currencyEl().getValue();
44626 restrictHeight : function()
44628 this.list.alignTo(this.currencyEl(), this.listAlign);
44629 this.list.alignTo(this.currencyEl(), this.listAlign);
44632 onViewClick : function(view, doFocus, el, e)
44634 var index = this.view.getSelectedIndexes()[0];
44636 var r = this.store.getAt(index);
44639 this.onSelect(r, index);
44643 onSelect : function(record, index){
44645 if(this.fireEvent('beforeselect', this, record, index) !== false){
44647 this.setFromCurrencyData(index > -1 ? record.data : false);
44651 this.fireEvent('select', this, record, index);
44655 setFromCurrencyData : function(o)
44659 this.lastCurrency = o;
44661 if (this.currencyField) {
44662 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44664 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44667 this.lastSelectionText = currency;
44669 //setting default currency
44670 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44671 this.setCurrency(this.defaultCurrency);
44675 this.setCurrency(currency);
44678 setFromData : function(o)
44682 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44684 this.setFromCurrencyData(c);
44689 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44691 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44694 this.setValue(value);
44698 setCurrency : function(v)
44700 this.currencyValue = v;
44703 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44708 setValue : function(v)
44710 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44716 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44718 this.inputEl().dom.value = (v == '') ? '' :
44719 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44721 if(!this.allowZero && v === '0') {
44722 this.hiddenEl().dom.value = '';
44723 this.inputEl().dom.value = '';
44730 getRawValue : function()
44732 var v = this.inputEl().getValue();
44737 getValue : function()
44739 return this.fixPrecision(this.parseValue(this.getRawValue()));
44742 parseValue : function(value)
44744 if(this.thousandsDelimiter) {
44746 r = new RegExp(",", "g");
44747 value = value.replace(r, "");
44750 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44751 return isNaN(value) ? '' : value;
44755 fixPrecision : function(value)
44757 if(this.thousandsDelimiter) {
44759 r = new RegExp(",", "g");
44760 value = value.replace(r, "");
44763 var nan = isNaN(value);
44765 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44766 return nan ? '' : value;
44768 return parseFloat(value).toFixed(this.decimalPrecision);
44771 decimalPrecisionFcn : function(v)
44773 return Math.floor(v);
44776 validateValue : function(value)
44778 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44782 var num = this.parseValue(value);
44785 this.markInvalid(String.format(this.nanText, value));
44789 if(num < this.minValue){
44790 this.markInvalid(String.format(this.minText, this.minValue));
44794 if(num > this.maxValue){
44795 this.markInvalid(String.format(this.maxText, this.maxValue));
44802 validate : function()
44804 if(this.disabled || this.allowBlank){
44809 var currency = this.getCurrency();
44811 if(this.validateValue(this.getRawValue()) && currency.length){
44816 this.markInvalid();
44820 getName: function()
44825 beforeBlur : function()
44831 var v = this.parseValue(this.getRawValue());
44838 onBlur : function()
44842 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44843 //this.el.removeClass(this.focusClass);
44846 this.hasFocus = false;
44848 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44852 var v = this.getValue();
44854 if(String(v) !== String(this.startValue)){
44855 this.fireEvent('change', this, v, this.startValue);
44858 this.fireEvent("blur", this);
44861 inputEl : function()
44863 return this.el.select('.roo-money-amount-input', true).first();
44866 currencyEl : function()
44868 return this.el.select('.roo-money-currency-input', true).first();
44871 hiddenEl : function()
44873 return this.el.select('input.hidden-number-input',true).first();
44877 * @class Roo.bootstrap.BezierSignature
44878 * @extends Roo.bootstrap.Component
44879 * Bootstrap BezierSignature class
44880 * This script refer to:
44881 * Title: Signature Pad
44883 * Availability: https://github.com/szimek/signature_pad
44886 * Create a new BezierSignature
44887 * @param {Object} config The config object
44890 Roo.bootstrap.BezierSignature = function(config){
44891 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44897 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44904 mouse_btn_down: true,
44907 * @cfg {int} canvas height
44909 canvas_height: '200px',
44912 * @cfg {float|function} Radius of a single dot.
44917 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44922 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44927 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44932 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44937 * @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.
44939 bg_color: 'rgba(0, 0, 0, 0)',
44942 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44944 dot_color: 'black',
44947 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44949 velocity_filter_weight: 0.7,
44952 * @cfg {function} Callback when stroke begin.
44957 * @cfg {function} Callback when stroke end.
44961 getAutoCreate : function()
44963 var cls = 'roo-signature column';
44966 cls += ' ' + this.cls;
44976 for(var i = 0; i < col_sizes.length; i++) {
44977 if(this[col_sizes[i]]) {
44978 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44988 cls: 'roo-signature-body',
44992 cls: 'roo-signature-body-canvas',
44993 height: this.canvas_height,
44994 width: this.canvas_width
45001 style: 'display: none'
45009 initEvents: function()
45011 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45013 var canvas = this.canvasEl();
45015 // mouse && touch event swapping...
45016 canvas.dom.style.touchAction = 'none';
45017 canvas.dom.style.msTouchAction = 'none';
45019 this.mouse_btn_down = false;
45020 canvas.on('mousedown', this._handleMouseDown, this);
45021 canvas.on('mousemove', this._handleMouseMove, this);
45022 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45024 if (window.PointerEvent) {
45025 canvas.on('pointerdown', this._handleMouseDown, this);
45026 canvas.on('pointermove', this._handleMouseMove, this);
45027 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45030 if ('ontouchstart' in window) {
45031 canvas.on('touchstart', this._handleTouchStart, this);
45032 canvas.on('touchmove', this._handleTouchMove, this);
45033 canvas.on('touchend', this._handleTouchEnd, this);
45036 Roo.EventManager.onWindowResize(this.resize, this, true);
45038 // file input event
45039 this.fileEl().on('change', this.uploadImage, this);
45046 resize: function(){
45048 var canvas = this.canvasEl().dom;
45049 var ctx = this.canvasElCtx();
45050 var img_data = false;
45052 if(canvas.width > 0) {
45053 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45055 // setting canvas width will clean img data
45058 var style = window.getComputedStyle ?
45059 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45061 var padding_left = parseInt(style.paddingLeft) || 0;
45062 var padding_right = parseInt(style.paddingRight) || 0;
45064 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45067 ctx.putImageData(img_data, 0, 0);
45071 _handleMouseDown: function(e)
45073 if (e.browserEvent.which === 1) {
45074 this.mouse_btn_down = true;
45075 this.strokeBegin(e);
45079 _handleMouseMove: function (e)
45081 if (this.mouse_btn_down) {
45082 this.strokeMoveUpdate(e);
45086 _handleMouseUp: function (e)
45088 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45089 this.mouse_btn_down = false;
45094 _handleTouchStart: function (e) {
45096 e.preventDefault();
45097 if (e.browserEvent.targetTouches.length === 1) {
45098 // var touch = e.browserEvent.changedTouches[0];
45099 // this.strokeBegin(touch);
45101 this.strokeBegin(e); // assume e catching the correct xy...
45105 _handleTouchMove: function (e) {
45106 e.preventDefault();
45107 // var touch = event.targetTouches[0];
45108 // _this._strokeMoveUpdate(touch);
45109 this.strokeMoveUpdate(e);
45112 _handleTouchEnd: function (e) {
45113 var wasCanvasTouched = e.target === this.canvasEl().dom;
45114 if (wasCanvasTouched) {
45115 e.preventDefault();
45116 // var touch = event.changedTouches[0];
45117 // _this._strokeEnd(touch);
45122 reset: function () {
45123 this._lastPoints = [];
45124 this._lastVelocity = 0;
45125 this._lastWidth = (this.min_width + this.max_width) / 2;
45126 this.canvasElCtx().fillStyle = this.dot_color;
45129 strokeMoveUpdate: function(e)
45131 this.strokeUpdate(e);
45133 if (this.throttle) {
45134 this.throttleStroke(this.strokeUpdate, this.throttle);
45137 this.strokeUpdate(e);
45141 strokeBegin: function(e)
45143 var newPointGroup = {
45144 color: this.dot_color,
45148 if (typeof this.onBegin === 'function') {
45152 this.curve_data.push(newPointGroup);
45154 this.strokeUpdate(e);
45157 strokeUpdate: function(e)
45159 var rect = this.canvasEl().dom.getBoundingClientRect();
45160 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45161 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45162 var lastPoints = lastPointGroup.points;
45163 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45164 var isLastPointTooClose = lastPoint
45165 ? point.distanceTo(lastPoint) <= this.min_distance
45167 var color = lastPointGroup.color;
45168 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45169 var curve = this.addPoint(point);
45171 this.drawDot({color: color, point: point});
45174 this.drawCurve({color: color, curve: curve});
45184 strokeEnd: function(e)
45186 this.strokeUpdate(e);
45187 if (typeof this.onEnd === 'function') {
45192 addPoint: function (point) {
45193 var _lastPoints = this._lastPoints;
45194 _lastPoints.push(point);
45195 if (_lastPoints.length > 2) {
45196 if (_lastPoints.length === 3) {
45197 _lastPoints.unshift(_lastPoints[0]);
45199 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45200 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45201 _lastPoints.shift();
45207 calculateCurveWidths: function (startPoint, endPoint) {
45208 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45209 (1 - this.velocity_filter_weight) * this._lastVelocity;
45211 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45214 start: this._lastWidth
45217 this._lastVelocity = velocity;
45218 this._lastWidth = newWidth;
45222 drawDot: function (_a) {
45223 var color = _a.color, point = _a.point;
45224 var ctx = this.canvasElCtx();
45225 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45227 this.drawCurveSegment(point.x, point.y, width);
45229 ctx.fillStyle = color;
45233 drawCurve: function (_a) {
45234 var color = _a.color, curve = _a.curve;
45235 var ctx = this.canvasElCtx();
45236 var widthDelta = curve.endWidth - curve.startWidth;
45237 var drawSteps = Math.floor(curve.length()) * 2;
45239 ctx.fillStyle = color;
45240 for (var i = 0; i < drawSteps; i += 1) {
45241 var t = i / drawSteps;
45247 var x = uuu * curve.startPoint.x;
45248 x += 3 * uu * t * curve.control1.x;
45249 x += 3 * u * tt * curve.control2.x;
45250 x += ttt * curve.endPoint.x;
45251 var y = uuu * curve.startPoint.y;
45252 y += 3 * uu * t * curve.control1.y;
45253 y += 3 * u * tt * curve.control2.y;
45254 y += ttt * curve.endPoint.y;
45255 var width = curve.startWidth + ttt * widthDelta;
45256 this.drawCurveSegment(x, y, width);
45262 drawCurveSegment: function (x, y, width) {
45263 var ctx = this.canvasElCtx();
45265 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45266 this.is_empty = false;
45271 var ctx = this.canvasElCtx();
45272 var canvas = this.canvasEl().dom;
45273 ctx.fillStyle = this.bg_color;
45274 ctx.clearRect(0, 0, canvas.width, canvas.height);
45275 ctx.fillRect(0, 0, canvas.width, canvas.height);
45276 this.curve_data = [];
45278 this.is_empty = true;
45283 return this.el.select('input',true).first();
45286 canvasEl: function()
45288 return this.el.select('canvas',true).first();
45291 canvasElCtx: function()
45293 return this.el.select('canvas',true).first().dom.getContext('2d');
45296 getImage: function(type)
45298 if(this.is_empty) {
45303 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45306 drawFromImage: function(img_src)
45308 var img = new Image();
45310 img.onload = function(){
45311 this.canvasElCtx().drawImage(img, 0, 0);
45316 this.is_empty = false;
45319 selectImage: function()
45321 this.fileEl().dom.click();
45324 uploadImage: function(e)
45326 var reader = new FileReader();
45328 reader.onload = function(e){
45329 var img = new Image();
45330 img.onload = function(){
45332 this.canvasElCtx().drawImage(img, 0, 0);
45334 img.src = e.target.result;
45337 reader.readAsDataURL(e.target.files[0]);
45340 // Bezier Point Constructor
45341 Point: (function () {
45342 function Point(x, y, time) {
45345 this.time = time || Date.now();
45347 Point.prototype.distanceTo = function (start) {
45348 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45350 Point.prototype.equals = function (other) {
45351 return this.x === other.x && this.y === other.y && this.time === other.time;
45353 Point.prototype.velocityFrom = function (start) {
45354 return this.time !== start.time
45355 ? this.distanceTo(start) / (this.time - start.time)
45362 // Bezier Constructor
45363 Bezier: (function () {
45364 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45365 this.startPoint = startPoint;
45366 this.control2 = control2;
45367 this.control1 = control1;
45368 this.endPoint = endPoint;
45369 this.startWidth = startWidth;
45370 this.endWidth = endWidth;
45372 Bezier.fromPoints = function (points, widths, scope) {
45373 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45374 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45375 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45377 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45378 var dx1 = s1.x - s2.x;
45379 var dy1 = s1.y - s2.y;
45380 var dx2 = s2.x - s3.x;
45381 var dy2 = s2.y - s3.y;
45382 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45383 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45384 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45385 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45386 var dxm = m1.x - m2.x;
45387 var dym = m1.y - m2.y;
45388 var k = l2 / (l1 + l2);
45389 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45390 var tx = s2.x - cm.x;
45391 var ty = s2.y - cm.y;
45393 c1: new scope.Point(m1.x + tx, m1.y + ty),
45394 c2: new scope.Point(m2.x + tx, m2.y + ty)
45397 Bezier.prototype.length = function () {
45402 for (var i = 0; i <= steps; i += 1) {
45404 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45405 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45407 var xdiff = cx - px;
45408 var ydiff = cy - py;
45409 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45416 Bezier.prototype.point = function (t, start, c1, c2, end) {
45417 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45418 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45419 + (3.0 * c2 * (1.0 - t) * t * t)
45420 + (end * t * t * t);
45425 throttleStroke: function(fn, wait) {
45426 if (wait === void 0) { wait = 250; }
45428 var timeout = null;
45432 var later = function () {
45433 previous = Date.now();
45435 result = fn.apply(storedContext, storedArgs);
45437 storedContext = null;
45441 return function wrapper() {
45443 for (var _i = 0; _i < arguments.length; _i++) {
45444 args[_i] = arguments[_i];
45446 var now = Date.now();
45447 var remaining = wait - (now - previous);
45448 storedContext = this;
45450 if (remaining <= 0 || remaining > wait) {
45452 clearTimeout(timeout);
45456 result = fn.apply(storedContext, storedArgs);
45458 storedContext = null;
45462 else if (!timeout) {
45463 timeout = window.setTimeout(later, remaining);