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.
8572 * The text to display in a centered loading message box (defaults to 'Loading...')
8576 * @cfg {String} msgCls
8577 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8579 msgCls : 'x-mask-loading',
8582 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8588 * Disables the mask to prevent it from being displayed
8590 disable : function(){
8591 this.disabled = true;
8595 * Enables the mask so that it can be displayed
8597 enable : function(){
8598 this.disabled = false;
8601 onLoadException : function()
8605 if (typeof(arguments[3]) != 'undefined') {
8606 Roo.MessageBox.alert("Error loading",arguments[3]);
8610 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8611 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8618 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8623 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8627 onBeforeLoad : function(){
8629 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8634 destroy : function(){
8636 this.store.un('beforeload', this.onBeforeLoad, this);
8637 this.store.un('load', this.onLoad, this);
8638 this.store.un('loadexception', this.onLoadException, this);
8640 var um = this.el.getUpdateManager();
8641 um.un('beforeupdate', this.onBeforeLoad, this);
8642 um.un('update', this.onLoad, this);
8643 um.un('failure', this.onLoad, this);
8647 * @class Roo.bootstrap.Table
8649 * @extends Roo.bootstrap.Component
8650 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8651 * Similar to Roo.grid.Grid
8653 var table = Roo.factory({
8655 xns : Roo.bootstrap,
8656 autoSizeColumns: true,
8663 sortInfo : { direction : 'ASC', field: 'name' },
8665 xtype : 'HttpProxy',
8668 url : 'https://example.com/some.data.url.json'
8671 xtype : 'JsonReader',
8673 fields : [ 'id', 'name', whatever' ],
8680 xtype : 'ColumnModel',
8684 dataIndex : 'is_in_group',
8687 renderer : function(v, x , r) {
8689 return String.format("{0}", v)
8695 xtype : 'RowSelectionModel',
8696 xns : Roo.bootstrap.Table
8697 // you can add listeners to catch selection change here....
8703 grid.render(Roo.get("some-div"));
8706 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8711 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8712 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8713 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8715 * @cfg {String} cls table class
8718 * @cfg {boolean} striped Should the rows be alternative striped
8719 * @cfg {boolean} bordered Add borders to the table
8720 * @cfg {boolean} hover Add hover highlighting
8721 * @cfg {boolean} condensed Format condensed
8722 * @cfg {boolean} responsive Format condensed
8723 * @cfg {Boolean} loadMask (true|false) default false
8724 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8725 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8726 * @cfg {Boolean} rowSelection (true|false) default false
8727 * @cfg {Boolean} cellSelection (true|false) default false
8728 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8729 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8730 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8731 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8732 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8733 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8736 * Create a new Table
8737 * @param {Object} config The config object
8740 Roo.bootstrap.Table = function(config)
8742 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8745 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8746 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8747 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8748 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8750 this.view = this; // compat with grid.
8752 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8754 this.sm.grid = this;
8755 this.selModel = Roo.factory(this.sm, Roo.grid);
8756 this.sm = this.selModel;
8757 this.sm.xmodule = this.xmodule || false;
8760 if (this.cm && typeof(this.cm.config) == 'undefined') {
8761 this.colModel = new Roo.grid.ColumnModel(this.cm);
8762 this.cm = this.colModel;
8763 this.cm.xmodule = this.xmodule || false;
8766 this.store= Roo.factory(this.store, Roo.data);
8767 this.ds = this.store;
8768 this.ds.xmodule = this.xmodule || false;
8771 if (this.footer && this.store) {
8772 this.footer.dataSource = this.ds;
8773 this.footer = Roo.factory(this.footer);
8780 * Fires when a cell is clicked
8781 * @param {Roo.bootstrap.Table} this
8782 * @param {Roo.Element} el
8783 * @param {Number} rowIndex
8784 * @param {Number} columnIndex
8785 * @param {Roo.EventObject} e
8789 * @event celldblclick
8790 * Fires when a cell is double clicked
8791 * @param {Roo.bootstrap.Table} this
8792 * @param {Roo.Element} el
8793 * @param {Number} rowIndex
8794 * @param {Number} columnIndex
8795 * @param {Roo.EventObject} e
8797 "celldblclick" : true,
8800 * Fires when a row is clicked
8801 * @param {Roo.bootstrap.Table} this
8802 * @param {Roo.Element} el
8803 * @param {Number} rowIndex
8804 * @param {Roo.EventObject} e
8808 * @event rowdblclick
8809 * Fires when a row is double clicked
8810 * @param {Roo.bootstrap.Table} this
8811 * @param {Roo.Element} el
8812 * @param {Number} rowIndex
8813 * @param {Roo.EventObject} e
8815 "rowdblclick" : true,
8818 * Fires when a mouseover occur
8819 * @param {Roo.bootstrap.Table} this
8820 * @param {Roo.Element} el
8821 * @param {Number} rowIndex
8822 * @param {Number} columnIndex
8823 * @param {Roo.EventObject} e
8828 * Fires when a mouseout occur
8829 * @param {Roo.bootstrap.Table} this
8830 * @param {Roo.Element} el
8831 * @param {Number} rowIndex
8832 * @param {Number} columnIndex
8833 * @param {Roo.EventObject} e
8838 * Fires when a row is rendered, so you can change add a style to it.
8839 * @param {Roo.bootstrap.Table} this
8840 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8844 * @event rowsrendered
8845 * Fires when all the rows have been rendered
8846 * @param {Roo.bootstrap.Table} this
8848 'rowsrendered' : true,
8850 * @event contextmenu
8851 * The raw contextmenu event for the entire grid.
8852 * @param {Roo.EventObject} e
8854 "contextmenu" : true,
8856 * @event rowcontextmenu
8857 * Fires when a row is right clicked
8858 * @param {Roo.bootstrap.Table} this
8859 * @param {Number} rowIndex
8860 * @param {Roo.EventObject} e
8862 "rowcontextmenu" : true,
8864 * @event cellcontextmenu
8865 * Fires when a cell is right clicked
8866 * @param {Roo.bootstrap.Table} this
8867 * @param {Number} rowIndex
8868 * @param {Number} cellIndex
8869 * @param {Roo.EventObject} e
8871 "cellcontextmenu" : true,
8873 * @event headercontextmenu
8874 * Fires when a header is right clicked
8875 * @param {Roo.bootstrap.Table} this
8876 * @param {Number} columnIndex
8877 * @param {Roo.EventObject} e
8879 "headercontextmenu" : true,
8882 * The raw mousedown event for the entire grid.
8883 * @param {Roo.EventObject} e
8890 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8906 enableColumnResize: true,
8908 rowSelection : false,
8909 cellSelection : false,
8912 minColumnWidth : 50,
8914 // Roo.Element - the tbody
8915 bodyEl: false, // <tbody> Roo.Element - thead element
8916 headEl: false, // <thead> Roo.Element - thead element
8917 resizeProxy : false, // proxy element for dragging?
8921 container: false, // used by gridpanel...
8927 auto_hide_footer : false,
8929 view: false, // actually points to this..
8931 getAutoCreate : function()
8933 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8940 // this get's auto added by panel.Grid
8941 if (this.scrollBody) {
8942 cfg.cls += ' table-body-fixed';
8945 cfg.cls += ' table-striped';
8949 cfg.cls += ' table-hover';
8951 if (this.bordered) {
8952 cfg.cls += ' table-bordered';
8954 if (this.condensed) {
8955 cfg.cls += ' table-condensed';
8958 if (this.responsive) {
8959 cfg.cls += ' table-responsive';
8963 cfg.cls+= ' ' +this.cls;
8969 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8972 if(this.store || this.cm){
8973 if(this.headerShow){
8974 cfg.cn.push(this.renderHeader());
8977 cfg.cn.push(this.renderBody());
8979 if(this.footerShow){
8980 cfg.cn.push(this.renderFooter());
8982 // where does this come from?
8983 //cfg.cls+= ' TableGrid';
8986 return { cn : [ cfg ] };
8989 initEvents : function()
8991 if(!this.store || !this.cm){
8994 if (this.selModel) {
8995 this.selModel.initEvents();
8999 //Roo.log('initEvents with ds!!!!');
9001 this.bodyEl = this.el.select('tbody', true).first();
9002 this.headEl = this.el.select('thead', true).first();
9003 this.mainFoot = this.el.select('tfoot', true).first();
9008 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9009 e.on('click', this.sort, this);
9013 // why is this done????? = it breaks dialogs??
9014 //this.parent().el.setStyle('position', 'relative');
9018 this.footer.parentId = this.id;
9019 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9022 this.el.select('tfoot tr td').first().addClass('hide');
9027 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9030 this.store.on('load', this.onLoad, this);
9031 this.store.on('beforeload', this.onBeforeLoad, this);
9032 this.store.on('update', this.onUpdate, this);
9033 this.store.on('add', this.onAdd, this);
9034 this.store.on("clear", this.clear, this);
9036 this.el.on("contextmenu", this.onContextMenu, this);
9039 this.cm.on("headerchange", this.onHeaderChange, this);
9040 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9042 //?? does bodyEl get replaced on render?
9043 this.bodyEl.on("click", this.onClick, this);
9044 this.bodyEl.on("dblclick", this.onDblClick, this);
9045 this.bodyEl.on('scroll', this.onBodyScroll, this);
9047 // guessing mainbody will work - this relays usually caught by selmodel at present.
9048 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9051 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9054 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9055 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9060 // Compatibility with grid - we implement all the view features at present.
9061 getView : function()
9066 initCSS : function()
9070 var cm = this.cm, styles = [];
9071 this.CSS.removeStyleSheet(this.id + '-cssrules');
9072 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9073 // we can honour xs/sm/md/xl as widths...
9074 // we first have to decide what widht we are currently at...
9075 var sz = Roo.getGridSize();
9079 var cols = []; // visable cols.
9081 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9082 var w = cm.getColumnWidth(i, false);
9084 cols.push( { rel : false, abs : 0 });
9088 cols.push( { rel : false, abs : w });
9090 last = i; // not really..
9093 var w = cm.getColumnWidth(i, sz);
9098 cols.push( { rel : w, abs : false });
9101 var avail = this.bodyEl.dom.clientWidth - total_abs;
9103 var unitWidth = Math.floor(avail / total);
9104 var rem = avail - (unitWidth * total);
9106 var hidden, width, pos = 0 , splithide , left;
9107 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9109 hidden = 'display:none;';
9111 width = 'width:0px;';
9113 if(!cm.isHidden(i)){
9117 // we can honour xs/sm/md/xl ?
9118 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9120 hidden = 'display:none;';
9122 // width should return a small number...
9124 w+=rem; // add the remaining with..
9127 left = "left:" + (pos -4) + "px;";
9128 width = "width:" + w+ "px;";
9131 if (this.responsive) {
9134 hidden = cm.isHidden(i) ? 'display:none' : '';
9135 splithide = 'display: none';
9138 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9141 splithide = 'display:none;';
9144 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9145 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9150 //Roo.log(styles.join(''));
9151 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9157 onContextMenu : function(e, t)
9159 this.processEvent("contextmenu", e);
9162 processEvent : function(name, e)
9164 if (name != 'touchstart' ) {
9165 this.fireEvent(name, e);
9168 var t = e.getTarget();
9170 var cell = Roo.get(t);
9176 if(cell.findParent('tfoot', false, true)){
9180 if(cell.findParent('thead', false, true)){
9182 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9183 cell = Roo.get(t).findParent('th', false, true);
9185 Roo.log("failed to find th in thead?");
9186 Roo.log(e.getTarget());
9191 var cellIndex = cell.dom.cellIndex;
9193 var ename = name == 'touchstart' ? 'click' : name;
9194 this.fireEvent("header" + ename, this, cellIndex, e);
9199 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9200 cell = Roo.get(t).findParent('td', false, true);
9202 Roo.log("failed to find th in tbody?");
9203 Roo.log(e.getTarget());
9208 var row = cell.findParent('tr', false, true);
9209 var cellIndex = cell.dom.cellIndex;
9210 var rowIndex = row.dom.rowIndex - 1;
9214 this.fireEvent("row" + name, this, rowIndex, e);
9218 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9224 onMouseover : function(e, el)
9226 var cell = Roo.get(el);
9232 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9233 cell = cell.findParent('td', false, true);
9236 var row = cell.findParent('tr', false, true);
9237 var cellIndex = cell.dom.cellIndex;
9238 var rowIndex = row.dom.rowIndex - 1; // start from 0
9240 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9244 onMouseout : function(e, el)
9246 var cell = Roo.get(el);
9252 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9253 cell = cell.findParent('td', false, true);
9256 var row = cell.findParent('tr', false, true);
9257 var cellIndex = cell.dom.cellIndex;
9258 var rowIndex = row.dom.rowIndex - 1; // start from 0
9260 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9264 onClick : function(e, el)
9266 var cell = Roo.get(el);
9268 if(!cell || (!this.cellSelection && !this.rowSelection)){
9272 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9273 cell = cell.findParent('td', false, true);
9276 if(!cell || typeof(cell) == 'undefined'){
9280 var row = cell.findParent('tr', false, true);
9282 if(!row || typeof(row) == 'undefined'){
9286 var cellIndex = cell.dom.cellIndex;
9287 var rowIndex = this.getRowIndex(row);
9289 // why??? - should these not be based on SelectionModel?
9290 //if(this.cellSelection){
9291 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9294 //if(this.rowSelection){
9295 this.fireEvent('rowclick', this, row, rowIndex, e);
9300 onDblClick : function(e,el)
9302 var cell = Roo.get(el);
9304 if(!cell || (!this.cellSelection && !this.rowSelection)){
9308 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9309 cell = cell.findParent('td', false, true);
9312 if(!cell || typeof(cell) == 'undefined'){
9316 var row = cell.findParent('tr', false, true);
9318 if(!row || typeof(row) == 'undefined'){
9322 var cellIndex = cell.dom.cellIndex;
9323 var rowIndex = this.getRowIndex(row);
9325 if(this.cellSelection){
9326 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9329 if(this.rowSelection){
9330 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9333 findRowIndex : function(el)
9335 var cell = Roo.get(el);
9339 var row = cell.findParent('tr', false, true);
9341 if(!row || typeof(row) == 'undefined'){
9344 return this.getRowIndex(row);
9346 sort : function(e,el)
9348 var col = Roo.get(el);
9350 if(!col.hasClass('sortable')){
9354 var sort = col.attr('sort');
9357 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9361 this.store.sortInfo = {field : sort, direction : dir};
9364 Roo.log("calling footer first");
9365 this.footer.onClick('first');
9368 this.store.load({ params : { start : 0 } });
9372 renderHeader : function()
9380 this.totalWidth = 0;
9382 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9384 var config = cm.config[i];
9388 cls : 'x-hcol-' + i,
9391 html: cm.getColumnHeader(i)
9394 var tooltip = cm.getColumnTooltip(i);
9396 c.tooltip = tooltip;
9402 if(typeof(config.sortable) != 'undefined' && config.sortable){
9403 c.cls += ' sortable';
9404 c.html = '<i class="fa"></i>' + c.html;
9407 // could use BS4 hidden-..-down
9409 if(typeof(config.lgHeader) != 'undefined'){
9410 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9413 if(typeof(config.mdHeader) != 'undefined'){
9414 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9417 if(typeof(config.smHeader) != 'undefined'){
9418 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9421 if(typeof(config.xsHeader) != 'undefined'){
9422 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9429 if(typeof(config.tooltip) != 'undefined'){
9430 c.tooltip = config.tooltip;
9433 if(typeof(config.colspan) != 'undefined'){
9434 c.colspan = config.colspan;
9437 // hidden is handled by CSS now
9439 if(typeof(config.dataIndex) != 'undefined'){
9440 c.sort = config.dataIndex;
9445 if(typeof(config.align) != 'undefined' && config.align.length){
9446 c.style += ' text-align:' + config.align + ';';
9449 /* width is done in CSS
9450 *if(typeof(config.width) != 'undefined'){
9451 c.style += ' width:' + config.width + 'px;';
9452 this.totalWidth += config.width;
9454 this.totalWidth += 100; // assume minimum of 100 per column?
9458 if(typeof(config.cls) != 'undefined'){
9459 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9461 // this is the bit that doesnt reall work at all...
9463 if (this.responsive) {
9466 ['xs','sm','md','lg'].map(function(size){
9468 if(typeof(config[size]) == 'undefined'){
9472 if (!config[size]) { // 0 = hidden
9473 // BS 4 '0' is treated as hide that column and below.
9474 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9478 c.cls += ' col-' + size + '-' + config[size] + (
9479 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9487 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9498 renderBody : function()
9508 colspan : this.cm.getColumnCount()
9518 renderFooter : function()
9528 colspan : this.cm.getColumnCount()
9542 // Roo.log('ds onload');
9547 var ds = this.store;
9549 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9550 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9551 if (_this.store.sortInfo) {
9553 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9554 e.select('i', true).addClass(['fa-arrow-up']);
9557 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9558 e.select('i', true).addClass(['fa-arrow-down']);
9563 var tbody = this.bodyEl;
9565 if(ds.getCount() > 0){
9566 ds.data.each(function(d,rowIndex){
9567 var row = this.renderRow(cm, ds, rowIndex);
9569 tbody.createChild(row);
9573 if(row.cellObjects.length){
9574 Roo.each(row.cellObjects, function(r){
9575 _this.renderCellObject(r);
9582 var tfoot = this.el.select('tfoot', true).first();
9584 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9586 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9588 var total = this.ds.getTotalCount();
9590 if(this.footer.pageSize < total){
9591 this.mainFoot.show();
9595 Roo.each(this.el.select('tbody td', true).elements, function(e){
9596 e.on('mouseover', _this.onMouseover, _this);
9599 Roo.each(this.el.select('tbody td', true).elements, function(e){
9600 e.on('mouseout', _this.onMouseout, _this);
9602 this.fireEvent('rowsrendered', this);
9606 this.initCSS(); /// resize cols
9612 onUpdate : function(ds,record)
9614 this.refreshRow(record);
9618 onRemove : function(ds, record, index, isUpdate){
9619 if(isUpdate !== true){
9620 this.fireEvent("beforerowremoved", this, index, record);
9622 var bt = this.bodyEl.dom;
9624 var rows = this.el.select('tbody > tr', true).elements;
9626 if(typeof(rows[index]) != 'undefined'){
9627 bt.removeChild(rows[index].dom);
9630 // if(bt.rows[index]){
9631 // bt.removeChild(bt.rows[index]);
9634 if(isUpdate !== true){
9635 //this.stripeRows(index);
9636 //this.syncRowHeights(index, index);
9638 this.fireEvent("rowremoved", this, index, record);
9642 onAdd : function(ds, records, rowIndex)
9644 //Roo.log('on Add called');
9645 // - note this does not handle multiple adding very well..
9646 var bt = this.bodyEl.dom;
9647 for (var i =0 ; i < records.length;i++) {
9648 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9649 //Roo.log(records[i]);
9650 //Roo.log(this.store.getAt(rowIndex+i));
9651 this.insertRow(this.store, rowIndex + i, false);
9658 refreshRow : function(record){
9659 var ds = this.store, index;
9660 if(typeof record == 'number'){
9662 record = ds.getAt(index);
9664 index = ds.indexOf(record);
9666 return; // should not happen - but seems to
9669 this.insertRow(ds, index, true);
9671 this.onRemove(ds, record, index+1, true);
9673 //this.syncRowHeights(index, index);
9675 this.fireEvent("rowupdated", this, index, record);
9677 // private - called by RowSelection
9678 onRowSelect : function(rowIndex){
9679 var row = this.getRowDom(rowIndex);
9680 row.addClass(['bg-info','info']);
9682 // private - called by RowSelection
9683 onRowDeselect : function(rowIndex)
9688 var row = this.getRowDom(rowIndex);
9689 row.removeClass(['bg-info','info']);
9692 * Focuses the specified row.
9693 * @param {Number} row The row index
9695 focusRow : function(row)
9697 //Roo.log('GridView.focusRow');
9698 var x = this.bodyEl.dom.scrollLeft;
9699 this.focusCell(row, 0, false);
9700 this.bodyEl.dom.scrollLeft = x;
9704 * Focuses the specified cell.
9705 * @param {Number} row The row index
9706 * @param {Number} col The column index
9707 * @param {Boolean} hscroll false to disable horizontal scrolling
9709 focusCell : function(row, col, hscroll)
9711 //Roo.log('GridView.focusCell');
9712 var el = this.ensureVisible(row, col, hscroll);
9713 // not sure what focusEL achives = it's a <a> pos relative
9714 //this.focusEl.alignTo(el, "tl-tl");
9716 // this.focusEl.focus();
9718 // this.focusEl.focus.defer(1, this.focusEl);
9723 * Scrolls the specified cell into view
9724 * @param {Number} row The row index
9725 * @param {Number} col The column index
9726 * @param {Boolean} hscroll false to disable horizontal scrolling
9728 ensureVisible : function(row, col, hscroll)
9730 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9731 //return null; //disable for testing.
9732 if(typeof row != "number"){
9735 if(row < 0 && row >= this.ds.getCount()){
9738 col = (col !== undefined ? col : 0);
9740 while(cm.isHidden(col)){
9744 var el = this.getCellDom(row, col);
9748 var c = this.bodyEl.dom;
9750 var ctop = parseInt(el.offsetTop, 10);
9751 var cleft = parseInt(el.offsetLeft, 10);
9752 var cbot = ctop + el.offsetHeight;
9753 var cright = cleft + el.offsetWidth;
9755 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9756 var ch = 0; //?? header is not withing the area?
9757 var stop = parseInt(c.scrollTop, 10);
9758 var sleft = parseInt(c.scrollLeft, 10);
9759 var sbot = stop + ch;
9760 var sright = sleft + c.clientWidth;
9762 Roo.log('GridView.ensureVisible:' +
9764 ' c.clientHeight:' + c.clientHeight +
9765 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9774 //Roo.log("set scrolltop to ctop DISABLE?");
9775 }else if(cbot > sbot){
9776 //Roo.log("set scrolltop to cbot-ch");
9777 c.scrollTop = cbot-ch;
9780 if(hscroll !== false){
9782 c.scrollLeft = cleft;
9783 }else if(cright > sright){
9784 c.scrollLeft = cright-c.clientWidth;
9792 insertRow : function(dm, rowIndex, isUpdate){
9795 this.fireEvent("beforerowsinserted", this, rowIndex);
9797 //var s = this.getScrollState();
9798 var row = this.renderRow(this.cm, this.store, rowIndex);
9799 // insert before rowIndex..
9800 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9804 if(row.cellObjects.length){
9805 Roo.each(row.cellObjects, function(r){
9806 _this.renderCellObject(r);
9811 this.fireEvent("rowsinserted", this, rowIndex);
9812 //this.syncRowHeights(firstRow, lastRow);
9813 //this.stripeRows(firstRow);
9820 getRowDom : function(rowIndex)
9822 var rows = this.el.select('tbody > tr', true).elements;
9824 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9827 getCellDom : function(rowIndex, colIndex)
9829 var row = this.getRowDom(rowIndex);
9830 if (row === false) {
9833 var cols = row.select('td', true).elements;
9834 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9838 // returns the object tree for a tr..
9841 renderRow : function(cm, ds, rowIndex)
9843 var d = ds.getAt(rowIndex);
9847 cls : 'x-row-' + rowIndex,
9851 var cellObjects = [];
9853 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9854 var config = cm.config[i];
9856 var renderer = cm.getRenderer(i);
9860 if(typeof(renderer) !== 'undefined'){
9861 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9863 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9864 // and are rendered into the cells after the row is rendered - using the id for the element.
9866 if(typeof(value) === 'object'){
9876 rowIndex : rowIndex,
9881 this.fireEvent('rowclass', this, rowcfg);
9885 // this might end up displaying HTML?
9886 // this is too messy... - better to only do it on columsn you know are going to be too long
9887 //tooltip : (typeof(value) === 'object') ? '' : value,
9888 cls : rowcfg.rowClass + ' x-col-' + i,
9890 html: (typeof(value) === 'object') ? '' : value
9897 if(typeof(config.colspan) != 'undefined'){
9898 td.colspan = config.colspan;
9903 if(typeof(config.align) != 'undefined' && config.align.length){
9904 td.style += ' text-align:' + config.align + ';';
9906 if(typeof(config.valign) != 'undefined' && config.valign.length){
9907 td.style += ' vertical-align:' + config.valign + ';';
9910 if(typeof(config.width) != 'undefined'){
9911 td.style += ' width:' + config.width + 'px;';
9915 if(typeof(config.cursor) != 'undefined'){
9916 td.style += ' cursor:' + config.cursor + ';';
9919 if(typeof(config.cls) != 'undefined'){
9920 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9922 if (this.responsive) {
9923 ['xs','sm','md','lg'].map(function(size){
9925 if(typeof(config[size]) == 'undefined'){
9931 if (!config[size]) { // 0 = hidden
9932 // BS 4 '0' is treated as hide that column and below.
9933 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9937 td.cls += ' col-' + size + '-' + config[size] + (
9938 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9948 row.cellObjects = cellObjects;
9956 onBeforeLoad : function()
9965 this.el.select('tbody', true).first().dom.innerHTML = '';
9968 * Show or hide a row.
9969 * @param {Number} rowIndex to show or hide
9970 * @param {Boolean} state hide
9972 setRowVisibility : function(rowIndex, state)
9974 var bt = this.bodyEl.dom;
9976 var rows = this.el.select('tbody > tr', true).elements;
9978 if(typeof(rows[rowIndex]) == 'undefined'){
9981 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9986 getSelectionModel : function(){
9988 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9990 return this.selModel;
9993 * Render the Roo.bootstrap object from renderder
9995 renderCellObject : function(r)
9999 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10001 var t = r.cfg.render(r.container);
10004 Roo.each(r.cfg.cn, function(c){
10006 container: t.getChildContainer(),
10009 _this.renderCellObject(child);
10014 * get the Row Index from a dom element.
10015 * @param {Roo.Element} row The row to look for
10016 * @returns {Number} the row
10018 getRowIndex : function(row)
10022 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10033 * get the header TH element for columnIndex
10034 * @param {Number} columnIndex
10035 * @returns {Roo.Element}
10037 getHeaderIndex: function(colIndex)
10039 var cols = this.headEl.select('th', true).elements;
10040 return cols[colIndex];
10043 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10044 * @param {domElement} cell to look for
10045 * @returns {Number} the column
10047 getCellIndex : function(cell)
10049 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10051 return parseInt(id[1], 10);
10056 * Returns the grid's underlying element = used by panel.Grid
10057 * @return {Element} The element
10059 getGridEl : function(){
10063 * Forces a resize - used by panel.Grid
10064 * @return {Element} The element
10066 autoSize : function()
10068 //var ctr = Roo.get(this.container.dom.parentElement);
10069 var ctr = Roo.get(this.el.dom);
10071 var thd = this.getGridEl().select('thead',true).first();
10072 var tbd = this.getGridEl().select('tbody', true).first();
10073 var tfd = this.getGridEl().select('tfoot', true).first();
10075 var cw = ctr.getWidth();
10076 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10080 tbd.setWidth(ctr.getWidth());
10081 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10082 // this needs fixing for various usage - currently only hydra job advers I think..
10084 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10086 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10089 cw = Math.max(cw, this.totalWidth);
10090 this.getGridEl().select('tbody tr',true).setWidth(cw);
10093 // resize 'expandable coloumn?
10095 return; // we doe not have a view in this design..
10098 onBodyScroll: function()
10100 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10102 this.headEl.setStyle({
10103 'position' : 'relative',
10104 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10110 var scrollHeight = this.bodyEl.dom.scrollHeight;
10112 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10114 var height = this.bodyEl.getHeight();
10116 if(scrollHeight - height == scrollTop) {
10118 var total = this.ds.getTotalCount();
10120 if(this.footer.cursor + this.footer.pageSize < total){
10122 this.footer.ds.load({
10124 start : this.footer.cursor + this.footer.pageSize,
10125 limit : this.footer.pageSize
10134 onColumnSplitterMoved : function(i, diff)
10136 this.userResized = true;
10138 var cm = this.colModel;
10140 var w = this.getHeaderIndex(i).getWidth() + diff;
10143 cm.setColumnWidth(i, w, true);
10145 //var cid = cm.getColumnId(i); << not used in this version?
10146 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10148 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10149 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10150 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10152 //this.updateSplitters();
10153 //this.layout(); << ??
10154 this.fireEvent("columnresize", i, w);
10156 onHeaderChange : function()
10158 var header = this.renderHeader();
10159 var table = this.el.select('table', true).first();
10161 this.headEl.remove();
10162 this.headEl = table.createChild(header, this.bodyEl, false);
10164 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10165 e.on('click', this.sort, this);
10168 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10169 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10174 onHiddenChange : function(colModel, colIndex, hidden)
10177 this.cm.setHidden()
10178 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10179 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10181 this.CSS.updateRule(thSelector, "display", "");
10182 this.CSS.updateRule(tdSelector, "display", "");
10185 this.CSS.updateRule(thSelector, "display", "none");
10186 this.CSS.updateRule(tdSelector, "display", "none");
10189 // onload calls initCSS()
10190 this.onHeaderChange();
10194 setColumnWidth: function(col_index, width)
10196 // width = "md-2 xs-2..."
10197 if(!this.colModel.config[col_index]) {
10201 var w = width.split(" ");
10203 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10205 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10208 for(var j = 0; j < w.length; j++) {
10214 var size_cls = w[j].split("-");
10216 if(!Number.isInteger(size_cls[1] * 1)) {
10220 if(!this.colModel.config[col_index][size_cls[0]]) {
10224 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10228 h_row[0].classList.replace(
10229 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10230 "col-"+size_cls[0]+"-"+size_cls[1]
10233 for(var i = 0; i < rows.length; i++) {
10235 var size_cls = w[j].split("-");
10237 if(!Number.isInteger(size_cls[1] * 1)) {
10241 if(!this.colModel.config[col_index][size_cls[0]]) {
10245 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10249 rows[i].classList.replace(
10250 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10251 "col-"+size_cls[0]+"-"+size_cls[1]
10255 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10260 // currently only used to find the split on drag..
10261 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10266 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10267 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10276 * @class Roo.bootstrap.TableCell
10277 * @extends Roo.bootstrap.Component
10278 * Bootstrap TableCell class
10279 * @cfg {String} html cell contain text
10280 * @cfg {String} cls cell class
10281 * @cfg {String} tag cell tag (td|th) default td
10282 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10283 * @cfg {String} align Aligns the content in a cell
10284 * @cfg {String} axis Categorizes cells
10285 * @cfg {String} bgcolor Specifies the background color of a cell
10286 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10287 * @cfg {Number} colspan Specifies the number of columns a cell should span
10288 * @cfg {String} headers Specifies one or more header cells a cell is related to
10289 * @cfg {Number} height Sets the height of a cell
10290 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10291 * @cfg {Number} rowspan Sets the number of rows a cell should span
10292 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10293 * @cfg {String} valign Vertical aligns the content in a cell
10294 * @cfg {Number} width Specifies the width of a cell
10297 * Create a new TableCell
10298 * @param {Object} config The config object
10301 Roo.bootstrap.TableCell = function(config){
10302 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10305 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10325 getAutoCreate : function(){
10326 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10333 cfg.tag = this.tag;
10346 cfg.align=this.align
10351 if (this.bgcolor) {
10352 cfg.bgcolor=this.bgcolor
10354 if (this.charoff) {
10355 cfg.charoff=this.charoff
10357 if (this.colspan) {
10358 cfg.colspan=this.colspan
10360 if (this.headers) {
10361 cfg.headers=this.headers
10364 cfg.height=this.height
10367 cfg.nowrap=this.nowrap
10369 if (this.rowspan) {
10370 cfg.rowspan=this.rowspan
10373 cfg.scope=this.scope
10376 cfg.valign=this.valign
10379 cfg.width=this.width
10398 * @class Roo.bootstrap.TableRow
10399 * @extends Roo.bootstrap.Component
10400 * Bootstrap TableRow class
10401 * @cfg {String} cls row class
10402 * @cfg {String} align Aligns the content in a table row
10403 * @cfg {String} bgcolor Specifies a background color for a table row
10404 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10405 * @cfg {String} valign Vertical aligns the content in a table row
10408 * Create a new TableRow
10409 * @param {Object} config The config object
10412 Roo.bootstrap.TableRow = function(config){
10413 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10416 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10424 getAutoCreate : function(){
10425 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10432 cfg.cls = this.cls;
10435 cfg.align = this.align;
10438 cfg.bgcolor = this.bgcolor;
10441 cfg.charoff = this.charoff;
10444 cfg.valign = this.valign;
10462 * @class Roo.bootstrap.TableBody
10463 * @extends Roo.bootstrap.Component
10464 * Bootstrap TableBody class
10465 * @cfg {String} cls element class
10466 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10467 * @cfg {String} align Aligns the content inside the element
10468 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10469 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10472 * Create a new TableBody
10473 * @param {Object} config The config object
10476 Roo.bootstrap.TableBody = function(config){
10477 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10480 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10488 getAutoCreate : function(){
10489 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10499 cfg.tag = this.tag;
10503 cfg.align = this.align;
10506 cfg.charoff = this.charoff;
10509 cfg.valign = this.valign;
10516 // initEvents : function()
10519 // if(!this.store){
10523 // this.store = Roo.factory(this.store, Roo.data);
10524 // this.store.on('load', this.onLoad, this);
10526 // this.store.load();
10530 // onLoad: function ()
10532 // this.fireEvent('load', this);
10542 * Ext JS Library 1.1.1
10543 * Copyright(c) 2006-2007, Ext JS, LLC.
10545 * Originally Released Under LGPL - original licence link has changed is not relivant.
10548 * <script type="text/javascript">
10551 // as we use this in bootstrap.
10552 Roo.namespace('Roo.form');
10554 * @class Roo.form.Action
10555 * Internal Class used to handle form actions
10557 * @param {Roo.form.BasicForm} el The form element or its id
10558 * @param {Object} config Configuration options
10563 // define the action interface
10564 Roo.form.Action = function(form, options){
10566 this.options = options || {};
10569 * Client Validation Failed
10572 Roo.form.Action.CLIENT_INVALID = 'client';
10574 * Server Validation Failed
10577 Roo.form.Action.SERVER_INVALID = 'server';
10579 * Connect to Server Failed
10582 Roo.form.Action.CONNECT_FAILURE = 'connect';
10584 * Reading Data from Server Failed
10587 Roo.form.Action.LOAD_FAILURE = 'load';
10589 Roo.form.Action.prototype = {
10591 failureType : undefined,
10592 response : undefined,
10593 result : undefined,
10595 // interface method
10596 run : function(options){
10600 // interface method
10601 success : function(response){
10605 // interface method
10606 handleResponse : function(response){
10610 // default connection failure
10611 failure : function(response){
10613 this.response = response;
10614 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10615 this.form.afterAction(this, false);
10618 processResponse : function(response){
10619 this.response = response;
10620 if(!response.responseText){
10623 this.result = this.handleResponse(response);
10624 return this.result;
10627 // utility functions used internally
10628 getUrl : function(appendParams){
10629 var url = this.options.url || this.form.url || this.form.el.dom.action;
10631 var p = this.getParams();
10633 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10639 getMethod : function(){
10640 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10643 getParams : function(){
10644 var bp = this.form.baseParams;
10645 var p = this.options.params;
10647 if(typeof p == "object"){
10648 p = Roo.urlEncode(Roo.applyIf(p, bp));
10649 }else if(typeof p == 'string' && bp){
10650 p += '&' + Roo.urlEncode(bp);
10653 p = Roo.urlEncode(bp);
10658 createCallback : function(){
10660 success: this.success,
10661 failure: this.failure,
10663 timeout: (this.form.timeout*1000),
10664 upload: this.form.fileUpload ? this.success : undefined
10669 Roo.form.Action.Submit = function(form, options){
10670 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10673 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10676 haveProgress : false,
10677 uploadComplete : false,
10679 // uploadProgress indicator.
10680 uploadProgress : function()
10682 if (!this.form.progressUrl) {
10686 if (!this.haveProgress) {
10687 Roo.MessageBox.progress("Uploading", "Uploading");
10689 if (this.uploadComplete) {
10690 Roo.MessageBox.hide();
10694 this.haveProgress = true;
10696 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10698 var c = new Roo.data.Connection();
10700 url : this.form.progressUrl,
10705 success : function(req){
10706 //console.log(data);
10710 rdata = Roo.decode(req.responseText)
10712 Roo.log("Invalid data from server..");
10716 if (!rdata || !rdata.success) {
10718 Roo.MessageBox.alert(Roo.encode(rdata));
10721 var data = rdata.data;
10723 if (this.uploadComplete) {
10724 Roo.MessageBox.hide();
10729 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10730 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10733 this.uploadProgress.defer(2000,this);
10736 failure: function(data) {
10737 Roo.log('progress url failed ');
10748 // run get Values on the form, so it syncs any secondary forms.
10749 this.form.getValues();
10751 var o = this.options;
10752 var method = this.getMethod();
10753 var isPost = method == 'POST';
10754 if(o.clientValidation === false || this.form.isValid()){
10756 if (this.form.progressUrl) {
10757 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10758 (new Date() * 1) + '' + Math.random());
10763 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10764 form:this.form.el.dom,
10765 url:this.getUrl(!isPost),
10767 params:isPost ? this.getParams() : null,
10768 isUpload: this.form.fileUpload,
10769 formData : this.form.formData
10772 this.uploadProgress();
10774 }else if (o.clientValidation !== false){ // client validation failed
10775 this.failureType = Roo.form.Action.CLIENT_INVALID;
10776 this.form.afterAction(this, false);
10780 success : function(response)
10782 this.uploadComplete= true;
10783 if (this.haveProgress) {
10784 Roo.MessageBox.hide();
10788 var result = this.processResponse(response);
10789 if(result === true || result.success){
10790 this.form.afterAction(this, true);
10794 this.form.markInvalid(result.errors);
10795 this.failureType = Roo.form.Action.SERVER_INVALID;
10797 this.form.afterAction(this, false);
10799 failure : function(response)
10801 this.uploadComplete= true;
10802 if (this.haveProgress) {
10803 Roo.MessageBox.hide();
10806 this.response = response;
10807 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10808 this.form.afterAction(this, false);
10811 handleResponse : function(response){
10812 if(this.form.errorReader){
10813 var rs = this.form.errorReader.read(response);
10816 for(var i = 0, len = rs.records.length; i < len; i++) {
10817 var r = rs.records[i];
10818 errors[i] = r.data;
10821 if(errors.length < 1){
10825 success : rs.success,
10831 ret = Roo.decode(response.responseText);
10835 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10845 Roo.form.Action.Load = function(form, options){
10846 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10847 this.reader = this.form.reader;
10850 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10855 Roo.Ajax.request(Roo.apply(
10856 this.createCallback(), {
10857 method:this.getMethod(),
10858 url:this.getUrl(false),
10859 params:this.getParams()
10863 success : function(response){
10865 var result = this.processResponse(response);
10866 if(result === true || !result.success || !result.data){
10867 this.failureType = Roo.form.Action.LOAD_FAILURE;
10868 this.form.afterAction(this, false);
10871 this.form.clearInvalid();
10872 this.form.setValues(result.data);
10873 this.form.afterAction(this, true);
10876 handleResponse : function(response){
10877 if(this.form.reader){
10878 var rs = this.form.reader.read(response);
10879 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10881 success : rs.success,
10885 return Roo.decode(response.responseText);
10889 Roo.form.Action.ACTION_TYPES = {
10890 'load' : Roo.form.Action.Load,
10891 'submit' : Roo.form.Action.Submit
10900 * @class Roo.bootstrap.Form
10901 * @extends Roo.bootstrap.Component
10902 * Bootstrap Form class
10903 * @cfg {String} method GET | POST (default POST)
10904 * @cfg {String} labelAlign top | left (default top)
10905 * @cfg {String} align left | right - for navbars
10906 * @cfg {Boolean} loadMask load mask when submit (default true)
10910 * Create a new Form
10911 * @param {Object} config The config object
10915 Roo.bootstrap.Form = function(config){
10917 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10919 Roo.bootstrap.Form.popover.apply();
10923 * @event clientvalidation
10924 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10925 * @param {Form} this
10926 * @param {Boolean} valid true if the form has passed client-side validation
10928 clientvalidation: true,
10930 * @event beforeaction
10931 * Fires before any action is performed. Return false to cancel the action.
10932 * @param {Form} this
10933 * @param {Action} action The action to be performed
10935 beforeaction: true,
10937 * @event actionfailed
10938 * Fires when an action fails.
10939 * @param {Form} this
10940 * @param {Action} action The action that failed
10942 actionfailed : true,
10944 * @event actioncomplete
10945 * Fires when an action is completed.
10946 * @param {Form} this
10947 * @param {Action} action The action that completed
10949 actioncomplete : true
10953 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10956 * @cfg {String} method
10957 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10961 * @cfg {String} url
10962 * The URL to use for form actions if one isn't supplied in the action options.
10965 * @cfg {Boolean} fileUpload
10966 * Set to true if this form is a file upload.
10970 * @cfg {Object} baseParams
10971 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10975 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10979 * @cfg {Sting} align (left|right) for navbar forms
10984 activeAction : null,
10987 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10988 * element by passing it or its id or mask the form itself by passing in true.
10991 waitMsgTarget : false,
10996 * @cfg {Boolean} errorMask (true|false) default false
11001 * @cfg {Number} maskOffset Default 100
11006 * @cfg {Boolean} maskBody
11010 getAutoCreate : function(){
11014 method : this.method || 'POST',
11015 id : this.id || Roo.id(),
11018 if (this.parent().xtype.match(/^Nav/)) {
11019 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11023 if (this.labelAlign == 'left' ) {
11024 cfg.cls += ' form-horizontal';
11030 initEvents : function()
11032 this.el.on('submit', this.onSubmit, this);
11033 // this was added as random key presses on the form where triggering form submit.
11034 this.el.on('keypress', function(e) {
11035 if (e.getCharCode() != 13) {
11038 // we might need to allow it for textareas.. and some other items.
11039 // check e.getTarget().
11041 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11045 Roo.log("keypress blocked");
11047 e.preventDefault();
11053 onSubmit : function(e){
11058 * Returns true if client-side validation on the form is successful.
11061 isValid : function(){
11062 var items = this.getItems();
11064 var target = false;
11066 items.each(function(f){
11072 Roo.log('invalid field: ' + f.name);
11076 if(!target && f.el.isVisible(true)){
11082 if(this.errorMask && !valid){
11083 Roo.bootstrap.Form.popover.mask(this, target);
11090 * Returns true if any fields in this form have changed since their original load.
11093 isDirty : function(){
11095 var items = this.getItems();
11096 items.each(function(f){
11106 * Performs a predefined action (submit or load) or custom actions you define on this form.
11107 * @param {String} actionName The name of the action type
11108 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11109 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11110 * accept other config options):
11112 Property Type Description
11113 ---------------- --------------- ----------------------------------------------------------------------------------
11114 url String The url for the action (defaults to the form's url)
11115 method String The form method to use (defaults to the form's method, or POST if not defined)
11116 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11117 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11118 validate the form on the client (defaults to false)
11120 * @return {BasicForm} this
11122 doAction : function(action, options){
11123 if(typeof action == 'string'){
11124 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11126 if(this.fireEvent('beforeaction', this, action) !== false){
11127 this.beforeAction(action);
11128 action.run.defer(100, action);
11134 beforeAction : function(action){
11135 var o = action.options;
11140 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11142 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11145 // not really supported yet.. ??
11147 //if(this.waitMsgTarget === true){
11148 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11149 //}else if(this.waitMsgTarget){
11150 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11151 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11153 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11159 afterAction : function(action, success){
11160 this.activeAction = null;
11161 var o = action.options;
11166 Roo.get(document.body).unmask();
11172 //if(this.waitMsgTarget === true){
11173 // this.el.unmask();
11174 //}else if(this.waitMsgTarget){
11175 // this.waitMsgTarget.unmask();
11177 // Roo.MessageBox.updateProgress(1);
11178 // Roo.MessageBox.hide();
11185 Roo.callback(o.success, o.scope, [this, action]);
11186 this.fireEvent('actioncomplete', this, action);
11190 // failure condition..
11191 // we have a scenario where updates need confirming.
11192 // eg. if a locking scenario exists..
11193 // we look for { errors : { needs_confirm : true }} in the response.
11195 (typeof(action.result) != 'undefined') &&
11196 (typeof(action.result.errors) != 'undefined') &&
11197 (typeof(action.result.errors.needs_confirm) != 'undefined')
11200 Roo.log("not supported yet");
11203 Roo.MessageBox.confirm(
11204 "Change requires confirmation",
11205 action.result.errorMsg,
11210 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11220 Roo.callback(o.failure, o.scope, [this, action]);
11221 // show an error message if no failed handler is set..
11222 if (!this.hasListener('actionfailed')) {
11223 Roo.log("need to add dialog support");
11225 Roo.MessageBox.alert("Error",
11226 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11227 action.result.errorMsg :
11228 "Saving Failed, please check your entries or try again"
11233 this.fireEvent('actionfailed', this, action);
11238 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11239 * @param {String} id The value to search for
11242 findField : function(id){
11243 var items = this.getItems();
11244 var field = items.get(id);
11246 items.each(function(f){
11247 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11254 return field || null;
11257 * Mark fields in this form invalid in bulk.
11258 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11259 * @return {BasicForm} this
11261 markInvalid : function(errors){
11262 if(errors instanceof Array){
11263 for(var i = 0, len = errors.length; i < len; i++){
11264 var fieldError = errors[i];
11265 var f = this.findField(fieldError.id);
11267 f.markInvalid(fieldError.msg);
11273 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11274 field.markInvalid(errors[id]);
11278 //Roo.each(this.childForms || [], function (f) {
11279 // f.markInvalid(errors);
11286 * Set values for fields in this form in bulk.
11287 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11288 * @return {BasicForm} this
11290 setValues : function(values){
11291 if(values instanceof Array){ // array of objects
11292 for(var i = 0, len = values.length; i < len; i++){
11294 var f = this.findField(v.id);
11296 f.setValue(v.value);
11297 if(this.trackResetOnLoad){
11298 f.originalValue = f.getValue();
11302 }else{ // object hash
11305 if(typeof values[id] != 'function' && (field = this.findField(id))){
11307 if (field.setFromData &&
11308 field.valueField &&
11309 field.displayField &&
11310 // combos' with local stores can
11311 // be queried via setValue()
11312 // to set their value..
11313 (field.store && !field.store.isLocal)
11317 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11318 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11319 field.setFromData(sd);
11321 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11323 field.setFromData(values);
11326 field.setValue(values[id]);
11330 if(this.trackResetOnLoad){
11331 field.originalValue = field.getValue();
11337 //Roo.each(this.childForms || [], function (f) {
11338 // f.setValues(values);
11345 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11346 * they are returned as an array.
11347 * @param {Boolean} asString
11350 getValues : function(asString){
11351 //if (this.childForms) {
11352 // copy values from the child forms
11353 // Roo.each(this.childForms, function (f) {
11354 // this.setValues(f.getValues());
11360 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11361 if(asString === true){
11364 return Roo.urlDecode(fs);
11368 * Returns the fields in this form as an object with key/value pairs.
11369 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11372 getFieldValues : function(with_hidden)
11374 var items = this.getItems();
11376 items.each(function(f){
11378 if (!f.getName()) {
11382 var v = f.getValue();
11384 if (f.inputType =='radio') {
11385 if (typeof(ret[f.getName()]) == 'undefined') {
11386 ret[f.getName()] = ''; // empty..
11389 if (!f.el.dom.checked) {
11393 v = f.el.dom.value;
11397 if(f.xtype == 'MoneyField'){
11398 ret[f.currencyName] = f.getCurrency();
11401 // not sure if this supported any more..
11402 if ((typeof(v) == 'object') && f.getRawValue) {
11403 v = f.getRawValue() ; // dates..
11405 // combo boxes where name != hiddenName...
11406 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11407 ret[f.name] = f.getRawValue();
11409 ret[f.getName()] = v;
11416 * Clears all invalid messages in this form.
11417 * @return {BasicForm} this
11419 clearInvalid : function(){
11420 var items = this.getItems();
11422 items.each(function(f){
11430 * Resets this form.
11431 * @return {BasicForm} this
11433 reset : function(){
11434 var items = this.getItems();
11435 items.each(function(f){
11439 Roo.each(this.childForms || [], function (f) {
11447 getItems : function()
11449 var r=new Roo.util.MixedCollection(false, function(o){
11450 return o.id || (o.id = Roo.id());
11452 var iter = function(el) {
11459 Roo.each(el.items,function(e) {
11468 hideFields : function(items)
11470 Roo.each(items, function(i){
11472 var f = this.findField(i);
11483 showFields : function(items)
11485 Roo.each(items, function(i){
11487 var f = this.findField(i);
11500 Roo.apply(Roo.bootstrap.Form, {
11516 intervalID : false,
11522 if(this.isApplied){
11527 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11528 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11529 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11530 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11533 this.maskEl.top.enableDisplayMode("block");
11534 this.maskEl.left.enableDisplayMode("block");
11535 this.maskEl.bottom.enableDisplayMode("block");
11536 this.maskEl.right.enableDisplayMode("block");
11538 this.toolTip = new Roo.bootstrap.Tooltip({
11539 cls : 'roo-form-error-popover',
11541 'left' : ['r-l', [-2,0], 'right'],
11542 'right' : ['l-r', [2,0], 'left'],
11543 'bottom' : ['tl-bl', [0,2], 'top'],
11544 'top' : [ 'bl-tl', [0,-2], 'bottom']
11548 this.toolTip.render(Roo.get(document.body));
11550 this.toolTip.el.enableDisplayMode("block");
11552 Roo.get(document.body).on('click', function(){
11556 Roo.get(document.body).on('touchstart', function(){
11560 this.isApplied = true
11563 mask : function(form, target)
11567 this.target = target;
11569 if(!this.form.errorMask || !target.el){
11573 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11575 Roo.log(scrollable);
11577 var ot = this.target.el.calcOffsetsTo(scrollable);
11579 var scrollTo = ot[1] - this.form.maskOffset;
11581 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11583 scrollable.scrollTo('top', scrollTo);
11585 var box = this.target.el.getBox();
11587 var zIndex = Roo.bootstrap.Modal.zIndex++;
11590 this.maskEl.top.setStyle('position', 'absolute');
11591 this.maskEl.top.setStyle('z-index', zIndex);
11592 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11593 this.maskEl.top.setLeft(0);
11594 this.maskEl.top.setTop(0);
11595 this.maskEl.top.show();
11597 this.maskEl.left.setStyle('position', 'absolute');
11598 this.maskEl.left.setStyle('z-index', zIndex);
11599 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11600 this.maskEl.left.setLeft(0);
11601 this.maskEl.left.setTop(box.y - this.padding);
11602 this.maskEl.left.show();
11604 this.maskEl.bottom.setStyle('position', 'absolute');
11605 this.maskEl.bottom.setStyle('z-index', zIndex);
11606 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11607 this.maskEl.bottom.setLeft(0);
11608 this.maskEl.bottom.setTop(box.bottom + this.padding);
11609 this.maskEl.bottom.show();
11611 this.maskEl.right.setStyle('position', 'absolute');
11612 this.maskEl.right.setStyle('z-index', zIndex);
11613 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11614 this.maskEl.right.setLeft(box.right + this.padding);
11615 this.maskEl.right.setTop(box.y - this.padding);
11616 this.maskEl.right.show();
11618 this.toolTip.bindEl = this.target.el;
11620 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11622 var tip = this.target.blankText;
11624 if(this.target.getValue() !== '' ) {
11626 if (this.target.invalidText.length) {
11627 tip = this.target.invalidText;
11628 } else if (this.target.regexText.length){
11629 tip = this.target.regexText;
11633 this.toolTip.show(tip);
11635 this.intervalID = window.setInterval(function() {
11636 Roo.bootstrap.Form.popover.unmask();
11639 window.onwheel = function(){ return false;};
11641 (function(){ this.isMasked = true; }).defer(500, this);
11645 unmask : function()
11647 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11651 this.maskEl.top.setStyle('position', 'absolute');
11652 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11653 this.maskEl.top.hide();
11655 this.maskEl.left.setStyle('position', 'absolute');
11656 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11657 this.maskEl.left.hide();
11659 this.maskEl.bottom.setStyle('position', 'absolute');
11660 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11661 this.maskEl.bottom.hide();
11663 this.maskEl.right.setStyle('position', 'absolute');
11664 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11665 this.maskEl.right.hide();
11667 this.toolTip.hide();
11669 this.toolTip.el.hide();
11671 window.onwheel = function(){ return true;};
11673 if(this.intervalID){
11674 window.clearInterval(this.intervalID);
11675 this.intervalID = false;
11678 this.isMasked = false;
11688 * Ext JS Library 1.1.1
11689 * Copyright(c) 2006-2007, Ext JS, LLC.
11691 * Originally Released Under LGPL - original licence link has changed is not relivant.
11694 * <script type="text/javascript">
11697 * @class Roo.form.VTypes
11698 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11701 Roo.form.VTypes = function(){
11702 // closure these in so they are only created once.
11703 var alpha = /^[a-zA-Z_]+$/;
11704 var alphanum = /^[a-zA-Z0-9_]+$/;
11705 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11706 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11708 // All these messages and functions are configurable
11711 * The function used to validate email addresses
11712 * @param {String} value The email address
11714 'email' : function(v){
11715 return email.test(v);
11718 * The error text to display when the email validation function returns false
11721 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11723 * The keystroke filter mask to be applied on email input
11726 'emailMask' : /[a-z0-9_\.\-@]/i,
11729 * The function used to validate URLs
11730 * @param {String} value The URL
11732 'url' : function(v){
11733 return url.test(v);
11736 * The error text to display when the url validation function returns false
11739 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11742 * The function used to validate alpha values
11743 * @param {String} value The value
11745 'alpha' : function(v){
11746 return alpha.test(v);
11749 * The error text to display when the alpha validation function returns false
11752 'alphaText' : 'This field should only contain letters and _',
11754 * The keystroke filter mask to be applied on alpha input
11757 'alphaMask' : /[a-z_]/i,
11760 * The function used to validate alphanumeric values
11761 * @param {String} value The value
11763 'alphanum' : function(v){
11764 return alphanum.test(v);
11767 * The error text to display when the alphanumeric validation function returns false
11770 'alphanumText' : 'This field should only contain letters, numbers and _',
11772 * The keystroke filter mask to be applied on alphanumeric input
11775 'alphanumMask' : /[a-z0-9_]/i
11785 * @class Roo.bootstrap.Input
11786 * @extends Roo.bootstrap.Component
11787 * Bootstrap Input class
11788 * @cfg {Boolean} disabled is it disabled
11789 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11790 * @cfg {String} name name of the input
11791 * @cfg {string} fieldLabel - the label associated
11792 * @cfg {string} placeholder - placeholder to put in text.
11793 * @cfg {string} before - input group add on before
11794 * @cfg {string} after - input group add on after
11795 * @cfg {string} size - (lg|sm) or leave empty..
11796 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11797 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11798 * @cfg {Number} md colspan out of 12 for computer-sized screens
11799 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11800 * @cfg {string} value default value of the input
11801 * @cfg {Number} labelWidth set the width of label
11802 * @cfg {Number} labellg set the width of label (1-12)
11803 * @cfg {Number} labelmd set the width of label (1-12)
11804 * @cfg {Number} labelsm set the width of label (1-12)
11805 * @cfg {Number} labelxs set the width of label (1-12)
11806 * @cfg {String} labelAlign (top|left)
11807 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11808 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11809 * @cfg {String} indicatorpos (left|right) default left
11810 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11811 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11812 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11814 * @cfg {String} align (left|center|right) Default left
11815 * @cfg {Boolean} forceFeedback (true|false) Default false
11818 * Create a new Input
11819 * @param {Object} config The config object
11822 Roo.bootstrap.Input = function(config){
11824 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11829 * Fires when this field receives input focus.
11830 * @param {Roo.form.Field} this
11835 * Fires when this field loses input focus.
11836 * @param {Roo.form.Field} this
11840 * @event specialkey
11841 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11842 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11843 * @param {Roo.form.Field} this
11844 * @param {Roo.EventObject} e The event object
11849 * Fires just before the field blurs if the field value has changed.
11850 * @param {Roo.form.Field} this
11851 * @param {Mixed} newValue The new value
11852 * @param {Mixed} oldValue The original value
11857 * Fires after the field has been marked as invalid.
11858 * @param {Roo.form.Field} this
11859 * @param {String} msg The validation message
11864 * Fires after the field has been validated with no errors.
11865 * @param {Roo.form.Field} this
11870 * Fires after the key up
11871 * @param {Roo.form.Field} this
11872 * @param {Roo.EventObject} e The event Object
11877 * Fires after the user pastes into input
11878 * @param {Roo.form.Field} this
11879 * @param {Roo.EventObject} e The event Object
11885 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11887 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11888 automatic validation (defaults to "keyup").
11890 validationEvent : "keyup",
11892 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11894 validateOnBlur : true,
11896 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11898 validationDelay : 250,
11900 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11902 focusClass : "x-form-focus", // not needed???
11906 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11908 invalidClass : "has-warning",
11911 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11913 validClass : "has-success",
11916 * @cfg {Boolean} hasFeedback (true|false) default true
11918 hasFeedback : true,
11921 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11923 invalidFeedbackClass : "glyphicon-warning-sign",
11926 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11928 validFeedbackClass : "glyphicon-ok",
11931 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11933 selectOnFocus : false,
11936 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11940 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11945 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11947 disableKeyFilter : false,
11950 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11954 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11958 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11960 blankText : "Please complete this mandatory field",
11963 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11967 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11969 maxLength : Number.MAX_VALUE,
11971 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11973 minLengthText : "The minimum length for this field is {0}",
11975 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11977 maxLengthText : "The maximum length for this field is {0}",
11981 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11982 * If available, this function will be called only after the basic validators all return true, and will be passed the
11983 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11987 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11988 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11989 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11993 * @cfg {String} regexText -- Depricated - use Invalid Text
11998 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12004 autocomplete: false,
12008 inputType : 'text',
12011 placeholder: false,
12016 preventMark: false,
12017 isFormField : true,
12020 labelAlign : false,
12023 formatedValue : false,
12024 forceFeedback : false,
12026 indicatorpos : 'left',
12036 parentLabelAlign : function()
12039 while (parent.parent()) {
12040 parent = parent.parent();
12041 if (typeof(parent.labelAlign) !='undefined') {
12042 return parent.labelAlign;
12049 getAutoCreate : function()
12051 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12057 if(this.inputType != 'hidden'){
12058 cfg.cls = 'form-group' //input-group
12064 type : this.inputType,
12065 value : this.value,
12066 cls : 'form-control',
12067 placeholder : this.placeholder || '',
12068 autocomplete : this.autocomplete || 'new-password'
12070 if (this.inputType == 'file') {
12071 input.style = 'overflow:hidden'; // why not in CSS?
12074 if(this.capture.length){
12075 input.capture = this.capture;
12078 if(this.accept.length){
12079 input.accept = this.accept + "/*";
12083 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12086 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12087 input.maxLength = this.maxLength;
12090 if (this.disabled) {
12091 input.disabled=true;
12094 if (this.readOnly) {
12095 input.readonly=true;
12099 input.name = this.name;
12103 input.cls += ' input-' + this.size;
12107 ['xs','sm','md','lg'].map(function(size){
12108 if (settings[size]) {
12109 cfg.cls += ' col-' + size + '-' + settings[size];
12113 var inputblock = input;
12117 cls: 'glyphicon form-control-feedback'
12120 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12123 cls : 'has-feedback',
12131 if (this.before || this.after) {
12134 cls : 'input-group',
12138 if (this.before && typeof(this.before) == 'string') {
12140 inputblock.cn.push({
12142 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12146 if (this.before && typeof(this.before) == 'object') {
12147 this.before = Roo.factory(this.before);
12149 inputblock.cn.push({
12151 cls : 'roo-input-before input-group-prepend input-group-' +
12152 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12156 inputblock.cn.push(input);
12158 if (this.after && typeof(this.after) == 'string') {
12159 inputblock.cn.push({
12161 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12165 if (this.after && typeof(this.after) == 'object') {
12166 this.after = Roo.factory(this.after);
12168 inputblock.cn.push({
12170 cls : 'roo-input-after input-group-append input-group-' +
12171 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12175 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12176 inputblock.cls += ' has-feedback';
12177 inputblock.cn.push(feedback);
12182 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12183 tooltip : 'This field is required'
12185 if (this.allowBlank ) {
12186 indicator.style = this.allowBlank ? ' display:none' : '';
12188 if (align ==='left' && this.fieldLabel.length) {
12190 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12197 cls : 'control-label col-form-label',
12198 html : this.fieldLabel
12209 var labelCfg = cfg.cn[1];
12210 var contentCfg = cfg.cn[2];
12212 if(this.indicatorpos == 'right'){
12217 cls : 'control-label col-form-label',
12221 html : this.fieldLabel
12235 labelCfg = cfg.cn[0];
12236 contentCfg = cfg.cn[1];
12240 if(this.labelWidth > 12){
12241 labelCfg.style = "width: " + this.labelWidth + 'px';
12244 if(this.labelWidth < 13 && this.labelmd == 0){
12245 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12248 if(this.labellg > 0){
12249 labelCfg.cls += ' col-lg-' + this.labellg;
12250 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12253 if(this.labelmd > 0){
12254 labelCfg.cls += ' col-md-' + this.labelmd;
12255 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12258 if(this.labelsm > 0){
12259 labelCfg.cls += ' col-sm-' + this.labelsm;
12260 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12263 if(this.labelxs > 0){
12264 labelCfg.cls += ' col-xs-' + this.labelxs;
12265 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12269 } else if ( this.fieldLabel.length) {
12276 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12277 tooltip : 'This field is required',
12278 style : this.allowBlank ? ' display:none' : ''
12282 //cls : 'input-group-addon',
12283 html : this.fieldLabel
12291 if(this.indicatorpos == 'right'){
12296 //cls : 'input-group-addon',
12297 html : this.fieldLabel
12302 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12303 tooltip : 'This field is required',
12304 style : this.allowBlank ? ' display:none' : ''
12324 if (this.parentType === 'Navbar' && this.parent().bar) {
12325 cfg.cls += ' navbar-form';
12328 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12329 // on BS4 we do this only if not form
12330 cfg.cls += ' navbar-form';
12338 * return the real input element.
12340 inputEl: function ()
12342 return this.el.select('input.form-control',true).first();
12345 tooltipEl : function()
12347 return this.inputEl();
12350 indicatorEl : function()
12352 if (Roo.bootstrap.version == 4) {
12353 return false; // not enabled in v4 yet.
12356 var indicator = this.el.select('i.roo-required-indicator',true).first();
12366 setDisabled : function(v)
12368 var i = this.inputEl().dom;
12370 i.removeAttribute('disabled');
12374 i.setAttribute('disabled','true');
12376 initEvents : function()
12379 this.inputEl().on("keydown" , this.fireKey, this);
12380 this.inputEl().on("focus", this.onFocus, this);
12381 this.inputEl().on("blur", this.onBlur, this);
12383 this.inputEl().relayEvent('keyup', this);
12384 this.inputEl().relayEvent('paste', this);
12386 this.indicator = this.indicatorEl();
12388 if(this.indicator){
12389 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12392 // reference to original value for reset
12393 this.originalValue = this.getValue();
12394 //Roo.form.TextField.superclass.initEvents.call(this);
12395 if(this.validationEvent == 'keyup'){
12396 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12397 this.inputEl().on('keyup', this.filterValidation, this);
12399 else if(this.validationEvent !== false){
12400 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12403 if(this.selectOnFocus){
12404 this.on("focus", this.preFocus, this);
12407 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12408 this.inputEl().on("keypress", this.filterKeys, this);
12410 this.inputEl().relayEvent('keypress', this);
12413 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12414 this.el.on("click", this.autoSize, this);
12417 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12418 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12421 if (typeof(this.before) == 'object') {
12422 this.before.render(this.el.select('.roo-input-before',true).first());
12424 if (typeof(this.after) == 'object') {
12425 this.after.render(this.el.select('.roo-input-after',true).first());
12428 this.inputEl().on('change', this.onChange, this);
12431 filterValidation : function(e){
12432 if(!e.isNavKeyPress()){
12433 this.validationTask.delay(this.validationDelay);
12437 * Validates the field value
12438 * @return {Boolean} True if the value is valid, else false
12440 validate : function(){
12441 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12442 if(this.disabled || this.validateValue(this.getRawValue())){
12447 this.markInvalid();
12453 * Validates a value according to the field's validation rules and marks the field as invalid
12454 * if the validation fails
12455 * @param {Mixed} value The value to validate
12456 * @return {Boolean} True if the value is valid, else false
12458 validateValue : function(value)
12460 if(this.getVisibilityEl().hasClass('hidden')){
12464 if(value.length < 1) { // if it's blank
12465 if(this.allowBlank){
12471 if(value.length < this.minLength){
12474 if(value.length > this.maxLength){
12478 var vt = Roo.form.VTypes;
12479 if(!vt[this.vtype](value, this)){
12483 if(typeof this.validator == "function"){
12484 var msg = this.validator(value);
12488 if (typeof(msg) == 'string') {
12489 this.invalidText = msg;
12493 if(this.regex && !this.regex.test(value)){
12501 fireKey : function(e){
12502 //Roo.log('field ' + e.getKey());
12503 if(e.isNavKeyPress()){
12504 this.fireEvent("specialkey", this, e);
12507 focus : function (selectText){
12509 this.inputEl().focus();
12510 if(selectText === true){
12511 this.inputEl().dom.select();
12517 onFocus : function(){
12518 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12519 // this.el.addClass(this.focusClass);
12521 if(!this.hasFocus){
12522 this.hasFocus = true;
12523 this.startValue = this.getValue();
12524 this.fireEvent("focus", this);
12528 beforeBlur : Roo.emptyFn,
12532 onBlur : function(){
12534 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12535 //this.el.removeClass(this.focusClass);
12537 this.hasFocus = false;
12538 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12541 var v = this.getValue();
12542 if(String(v) !== String(this.startValue)){
12543 this.fireEvent('change', this, v, this.startValue);
12545 this.fireEvent("blur", this);
12548 onChange : function(e)
12550 var v = this.getValue();
12551 if(String(v) !== String(this.startValue)){
12552 this.fireEvent('change', this, v, this.startValue);
12558 * Resets the current field value to the originally loaded value and clears any validation messages
12560 reset : function(){
12561 this.setValue(this.originalValue);
12565 * Returns the name of the field
12566 * @return {Mixed} name The name field
12568 getName: function(){
12572 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12573 * @return {Mixed} value The field value
12575 getValue : function(){
12577 var v = this.inputEl().getValue();
12582 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12583 * @return {Mixed} value The field value
12585 getRawValue : function(){
12586 var v = this.inputEl().getValue();
12592 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12593 * @param {Mixed} value The value to set
12595 setRawValue : function(v){
12596 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12599 selectText : function(start, end){
12600 var v = this.getRawValue();
12602 start = start === undefined ? 0 : start;
12603 end = end === undefined ? v.length : end;
12604 var d = this.inputEl().dom;
12605 if(d.setSelectionRange){
12606 d.setSelectionRange(start, end);
12607 }else if(d.createTextRange){
12608 var range = d.createTextRange();
12609 range.moveStart("character", start);
12610 range.moveEnd("character", v.length-end);
12617 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12618 * @param {Mixed} value The value to set
12620 setValue : function(v){
12623 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12629 processValue : function(value){
12630 if(this.stripCharsRe){
12631 var newValue = value.replace(this.stripCharsRe, '');
12632 if(newValue !== value){
12633 this.setRawValue(newValue);
12640 preFocus : function(){
12642 if(this.selectOnFocus){
12643 this.inputEl().dom.select();
12646 filterKeys : function(e){
12647 var k = e.getKey();
12648 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12651 var c = e.getCharCode(), cc = String.fromCharCode(c);
12652 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12655 if(!this.maskRe.test(cc)){
12660 * Clear any invalid styles/messages for this field
12662 clearInvalid : function(){
12664 if(!this.el || this.preventMark){ // not rendered
12669 this.el.removeClass([this.invalidClass, 'is-invalid']);
12671 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12673 var feedback = this.el.select('.form-control-feedback', true).first();
12676 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12681 if(this.indicator){
12682 this.indicator.removeClass('visible');
12683 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12686 this.fireEvent('valid', this);
12690 * Mark this field as valid
12692 markValid : function()
12694 if(!this.el || this.preventMark){ // not rendered...
12698 this.el.removeClass([this.invalidClass, this.validClass]);
12699 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12701 var feedback = this.el.select('.form-control-feedback', true).first();
12704 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12707 if(this.indicator){
12708 this.indicator.removeClass('visible');
12709 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12717 if(this.allowBlank && !this.getRawValue().length){
12720 if (Roo.bootstrap.version == 3) {
12721 this.el.addClass(this.validClass);
12723 this.inputEl().addClass('is-valid');
12726 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12728 var feedback = this.el.select('.form-control-feedback', true).first();
12731 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12732 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12737 this.fireEvent('valid', this);
12741 * Mark this field as invalid
12742 * @param {String} msg The validation message
12744 markInvalid : function(msg)
12746 if(!this.el || this.preventMark){ // not rendered
12750 this.el.removeClass([this.invalidClass, this.validClass]);
12751 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12753 var feedback = this.el.select('.form-control-feedback', true).first();
12756 this.el.select('.form-control-feedback', true).first().removeClass(
12757 [this.invalidFeedbackClass, this.validFeedbackClass]);
12764 if(this.allowBlank && !this.getRawValue().length){
12768 if(this.indicator){
12769 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12770 this.indicator.addClass('visible');
12772 if (Roo.bootstrap.version == 3) {
12773 this.el.addClass(this.invalidClass);
12775 this.inputEl().addClass('is-invalid');
12780 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12782 var feedback = this.el.select('.form-control-feedback', true).first();
12785 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12787 if(this.getValue().length || this.forceFeedback){
12788 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12795 this.fireEvent('invalid', this, msg);
12798 SafariOnKeyDown : function(event)
12800 // this is a workaround for a password hang bug on chrome/ webkit.
12801 if (this.inputEl().dom.type != 'password') {
12805 var isSelectAll = false;
12807 if(this.inputEl().dom.selectionEnd > 0){
12808 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12810 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12811 event.preventDefault();
12816 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12818 event.preventDefault();
12819 // this is very hacky as keydown always get's upper case.
12821 var cc = String.fromCharCode(event.getCharCode());
12822 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12826 adjustWidth : function(tag, w){
12827 tag = tag.toLowerCase();
12828 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12829 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12830 if(tag == 'input'){
12833 if(tag == 'textarea'){
12836 }else if(Roo.isOpera){
12837 if(tag == 'input'){
12840 if(tag == 'textarea'){
12848 setFieldLabel : function(v)
12850 if(!this.rendered){
12854 if(this.indicatorEl()){
12855 var ar = this.el.select('label > span',true);
12857 if (ar.elements.length) {
12858 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12859 this.fieldLabel = v;
12863 var br = this.el.select('label',true);
12865 if(br.elements.length) {
12866 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12867 this.fieldLabel = v;
12871 Roo.log('Cannot Found any of label > span || label in input');
12875 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12876 this.fieldLabel = v;
12891 * @class Roo.bootstrap.TextArea
12892 * @extends Roo.bootstrap.Input
12893 * Bootstrap TextArea class
12894 * @cfg {Number} cols Specifies the visible width of a text area
12895 * @cfg {Number} rows Specifies the visible number of lines in a text area
12896 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12897 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12898 * @cfg {string} html text
12901 * Create a new TextArea
12902 * @param {Object} config The config object
12905 Roo.bootstrap.TextArea = function(config){
12906 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12910 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12920 getAutoCreate : function(){
12922 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12928 if(this.inputType != 'hidden'){
12929 cfg.cls = 'form-group' //input-group
12937 value : this.value || '',
12938 html: this.html || '',
12939 cls : 'form-control',
12940 placeholder : this.placeholder || ''
12944 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12945 input.maxLength = this.maxLength;
12949 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12953 input.cols = this.cols;
12956 if (this.readOnly) {
12957 input.readonly = true;
12961 input.name = this.name;
12965 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12969 ['xs','sm','md','lg'].map(function(size){
12970 if (settings[size]) {
12971 cfg.cls += ' col-' + size + '-' + settings[size];
12975 var inputblock = input;
12977 if(this.hasFeedback && !this.allowBlank){
12981 cls: 'glyphicon form-control-feedback'
12985 cls : 'has-feedback',
12994 if (this.before || this.after) {
12997 cls : 'input-group',
13001 inputblock.cn.push({
13003 cls : 'input-group-addon',
13008 inputblock.cn.push(input);
13010 if(this.hasFeedback && !this.allowBlank){
13011 inputblock.cls += ' has-feedback';
13012 inputblock.cn.push(feedback);
13016 inputblock.cn.push({
13018 cls : 'input-group-addon',
13025 if (align ==='left' && this.fieldLabel.length) {
13030 cls : 'control-label',
13031 html : this.fieldLabel
13042 if(this.labelWidth > 12){
13043 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13046 if(this.labelWidth < 13 && this.labelmd == 0){
13047 this.labelmd = this.labelWidth;
13050 if(this.labellg > 0){
13051 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13052 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13055 if(this.labelmd > 0){
13056 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13057 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13060 if(this.labelsm > 0){
13061 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13062 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13065 if(this.labelxs > 0){
13066 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13067 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13070 } else if ( this.fieldLabel.length) {
13075 //cls : 'input-group-addon',
13076 html : this.fieldLabel
13094 if (this.disabled) {
13095 input.disabled=true;
13102 * return the real textarea element.
13104 inputEl: function ()
13106 return this.el.select('textarea.form-control',true).first();
13110 * Clear any invalid styles/messages for this field
13112 clearInvalid : function()
13115 if(!this.el || this.preventMark){ // not rendered
13119 var label = this.el.select('label', true).first();
13120 var icon = this.el.select('i.fa-star', true).first();
13125 this.el.removeClass( this.validClass);
13126 this.inputEl().removeClass('is-invalid');
13128 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13130 var feedback = this.el.select('.form-control-feedback', true).first();
13133 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13138 this.fireEvent('valid', this);
13142 * Mark this field as valid
13144 markValid : function()
13146 if(!this.el || this.preventMark){ // not rendered
13150 this.el.removeClass([this.invalidClass, this.validClass]);
13151 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13153 var feedback = this.el.select('.form-control-feedback', true).first();
13156 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13159 if(this.disabled || this.allowBlank){
13163 var label = this.el.select('label', true).first();
13164 var icon = this.el.select('i.fa-star', true).first();
13169 if (Roo.bootstrap.version == 3) {
13170 this.el.addClass(this.validClass);
13172 this.inputEl().addClass('is-valid');
13176 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13178 var feedback = this.el.select('.form-control-feedback', true).first();
13181 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13182 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13187 this.fireEvent('valid', this);
13191 * Mark this field as invalid
13192 * @param {String} msg The validation message
13194 markInvalid : function(msg)
13196 if(!this.el || this.preventMark){ // not rendered
13200 this.el.removeClass([this.invalidClass, this.validClass]);
13201 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13203 var feedback = this.el.select('.form-control-feedback', true).first();
13206 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13209 if(this.disabled || this.allowBlank){
13213 var label = this.el.select('label', true).first();
13214 var icon = this.el.select('i.fa-star', true).first();
13216 if(!this.getValue().length && label && !icon){
13217 this.el.createChild({
13219 cls : 'text-danger fa fa-lg fa-star',
13220 tooltip : 'This field is required',
13221 style : 'margin-right:5px;'
13225 if (Roo.bootstrap.version == 3) {
13226 this.el.addClass(this.invalidClass);
13228 this.inputEl().addClass('is-invalid');
13231 // fixme ... this may be depricated need to test..
13232 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13234 var feedback = this.el.select('.form-control-feedback', true).first();
13237 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13239 if(this.getValue().length || this.forceFeedback){
13240 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13247 this.fireEvent('invalid', this, msg);
13255 * trigger field - base class for combo..
13260 * @class Roo.bootstrap.TriggerField
13261 * @extends Roo.bootstrap.Input
13262 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13263 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13264 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13265 * for which you can provide a custom implementation. For example:
13267 var trigger = new Roo.bootstrap.TriggerField();
13268 trigger.onTriggerClick = myTriggerFn;
13269 trigger.applyTo('my-field');
13272 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13273 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13274 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13275 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13276 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13279 * Create a new TriggerField.
13280 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13281 * to the base TextField)
13283 Roo.bootstrap.TriggerField = function(config){
13284 this.mimicing = false;
13285 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13288 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13290 * @cfg {String} triggerClass A CSS class to apply to the trigger
13293 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13298 * @cfg {Boolean} removable (true|false) special filter default false
13302 /** @cfg {Boolean} grow @hide */
13303 /** @cfg {Number} growMin @hide */
13304 /** @cfg {Number} growMax @hide */
13310 autoSize: Roo.emptyFn,
13314 deferHeight : true,
13317 actionMode : 'wrap',
13322 getAutoCreate : function(){
13324 var align = this.labelAlign || this.parentLabelAlign();
13329 cls: 'form-group' //input-group
13336 type : this.inputType,
13337 cls : 'form-control',
13338 autocomplete: 'new-password',
13339 placeholder : this.placeholder || ''
13343 input.name = this.name;
13346 input.cls += ' input-' + this.size;
13349 if (this.disabled) {
13350 input.disabled=true;
13353 var inputblock = input;
13355 if(this.hasFeedback && !this.allowBlank){
13359 cls: 'glyphicon form-control-feedback'
13362 if(this.removable && !this.editable ){
13364 cls : 'has-feedback',
13370 cls : 'roo-combo-removable-btn close'
13377 cls : 'has-feedback',
13386 if(this.removable && !this.editable ){
13388 cls : 'roo-removable',
13394 cls : 'roo-combo-removable-btn close'
13401 if (this.before || this.after) {
13404 cls : 'input-group',
13408 inputblock.cn.push({
13410 cls : 'input-group-addon input-group-prepend input-group-text',
13415 inputblock.cn.push(input);
13417 if(this.hasFeedback && !this.allowBlank){
13418 inputblock.cls += ' has-feedback';
13419 inputblock.cn.push(feedback);
13423 inputblock.cn.push({
13425 cls : 'input-group-addon input-group-append input-group-text',
13434 var ibwrap = inputblock;
13439 cls: 'roo-select2-choices',
13443 cls: 'roo-select2-search-field',
13455 cls: 'roo-select2-container input-group',
13460 cls: 'form-hidden-field'
13466 if(!this.multiple && this.showToggleBtn){
13472 if (this.caret != false) {
13475 cls: 'fa fa-' + this.caret
13482 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13484 Roo.bootstrap.version == 3 ? caret : '',
13487 cls: 'combobox-clear',
13501 combobox.cls += ' roo-select2-container-multi';
13505 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13506 tooltip : 'This field is required'
13508 if (Roo.bootstrap.version == 4) {
13511 style : 'display:none'
13516 if (align ==='left' && this.fieldLabel.length) {
13518 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13525 cls : 'control-label',
13526 html : this.fieldLabel
13538 var labelCfg = cfg.cn[1];
13539 var contentCfg = cfg.cn[2];
13541 if(this.indicatorpos == 'right'){
13546 cls : 'control-label',
13550 html : this.fieldLabel
13564 labelCfg = cfg.cn[0];
13565 contentCfg = cfg.cn[1];
13568 if(this.labelWidth > 12){
13569 labelCfg.style = "width: " + this.labelWidth + 'px';
13572 if(this.labelWidth < 13 && this.labelmd == 0){
13573 this.labelmd = this.labelWidth;
13576 if(this.labellg > 0){
13577 labelCfg.cls += ' col-lg-' + this.labellg;
13578 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13581 if(this.labelmd > 0){
13582 labelCfg.cls += ' col-md-' + this.labelmd;
13583 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13586 if(this.labelsm > 0){
13587 labelCfg.cls += ' col-sm-' + this.labelsm;
13588 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13591 if(this.labelxs > 0){
13592 labelCfg.cls += ' col-xs-' + this.labelxs;
13593 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13596 } else if ( this.fieldLabel.length) {
13597 // Roo.log(" label");
13602 //cls : 'input-group-addon',
13603 html : this.fieldLabel
13611 if(this.indicatorpos == 'right'){
13619 html : this.fieldLabel
13633 // Roo.log(" no label && no align");
13640 ['xs','sm','md','lg'].map(function(size){
13641 if (settings[size]) {
13642 cfg.cls += ' col-' + size + '-' + settings[size];
13653 onResize : function(w, h){
13654 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13655 // if(typeof w == 'number'){
13656 // var x = w - this.trigger.getWidth();
13657 // this.inputEl().setWidth(this.adjustWidth('input', x));
13658 // this.trigger.setStyle('left', x+'px');
13663 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13666 getResizeEl : function(){
13667 return this.inputEl();
13671 getPositionEl : function(){
13672 return this.inputEl();
13676 alignErrorIcon : function(){
13677 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13681 initEvents : function(){
13685 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13686 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13687 if(!this.multiple && this.showToggleBtn){
13688 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13689 if(this.hideTrigger){
13690 this.trigger.setDisplayed(false);
13692 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13696 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13699 if(this.removable && !this.editable && !this.tickable){
13700 var close = this.closeTriggerEl();
13703 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13704 close.on('click', this.removeBtnClick, this, close);
13708 //this.trigger.addClassOnOver('x-form-trigger-over');
13709 //this.trigger.addClassOnClick('x-form-trigger-click');
13712 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13716 closeTriggerEl : function()
13718 var close = this.el.select('.roo-combo-removable-btn', true).first();
13719 return close ? close : false;
13722 removeBtnClick : function(e, h, el)
13724 e.preventDefault();
13726 if(this.fireEvent("remove", this) !== false){
13728 this.fireEvent("afterremove", this)
13732 createList : function()
13734 this.list = Roo.get(document.body).createChild({
13735 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13736 cls: 'typeahead typeahead-long dropdown-menu shadow',
13737 style: 'display:none'
13740 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13745 initTrigger : function(){
13750 onDestroy : function(){
13752 this.trigger.removeAllListeners();
13753 // this.trigger.remove();
13756 // this.wrap.remove();
13758 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13762 onFocus : function(){
13763 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13765 if(!this.mimicing){
13766 this.wrap.addClass('x-trigger-wrap-focus');
13767 this.mimicing = true;
13768 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13769 if(this.monitorTab){
13770 this.el.on("keydown", this.checkTab, this);
13777 checkTab : function(e){
13778 if(e.getKey() == e.TAB){
13779 this.triggerBlur();
13784 onBlur : function(){
13789 mimicBlur : function(e, t){
13791 if(!this.wrap.contains(t) && this.validateBlur()){
13792 this.triggerBlur();
13798 triggerBlur : function(){
13799 this.mimicing = false;
13800 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13801 if(this.monitorTab){
13802 this.el.un("keydown", this.checkTab, this);
13804 //this.wrap.removeClass('x-trigger-wrap-focus');
13805 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13809 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13810 validateBlur : function(e, t){
13815 onDisable : function(){
13816 this.inputEl().dom.disabled = true;
13817 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13819 // this.wrap.addClass('x-item-disabled');
13824 onEnable : function(){
13825 this.inputEl().dom.disabled = false;
13826 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13828 // this.el.removeClass('x-item-disabled');
13833 onShow : function(){
13834 var ae = this.getActionEl();
13837 ae.dom.style.display = '';
13838 ae.dom.style.visibility = 'visible';
13844 onHide : function(){
13845 var ae = this.getActionEl();
13846 ae.dom.style.display = 'none';
13850 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13851 * by an implementing function.
13853 * @param {EventObject} e
13855 onTriggerClick : Roo.emptyFn
13863 * @class Roo.bootstrap.CardUploader
13864 * @extends Roo.bootstrap.Button
13865 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13866 * @cfg {Number} errorTimeout default 3000
13867 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13868 * @cfg {Array} html The button text.
13872 * Create a new CardUploader
13873 * @param {Object} config The config object
13876 Roo.bootstrap.CardUploader = function(config){
13880 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13883 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13891 * When a image is clicked on - and needs to display a slideshow or similar..
13892 * @param {Roo.bootstrap.Card} this
13893 * @param {Object} The image information data
13899 * When a the download link is clicked
13900 * @param {Roo.bootstrap.Card} this
13901 * @param {Object} The image information data contains
13908 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13911 errorTimeout : 3000,
13915 fileCollection : false,
13918 getAutoCreate : function()
13922 cls :'form-group' ,
13927 //cls : 'input-group-addon',
13928 html : this.fieldLabel
13936 value : this.value,
13937 cls : 'd-none form-control'
13942 multiple : 'multiple',
13944 cls : 'd-none roo-card-upload-selector'
13948 cls : 'roo-card-uploader-button-container w-100 mb-2'
13951 cls : 'card-columns roo-card-uploader-container'
13961 getChildContainer : function() /// what children are added to.
13963 return this.containerEl;
13966 getButtonContainer : function() /// what children are added to.
13968 return this.el.select(".roo-card-uploader-button-container").first();
13971 initEvents : function()
13974 Roo.bootstrap.Input.prototype.initEvents.call(this);
13978 xns: Roo.bootstrap,
13981 container_method : 'getButtonContainer' ,
13982 html : this.html, // fix changable?
13985 'click' : function(btn, e) {
13994 this.urlAPI = (window.createObjectURL && window) ||
13995 (window.URL && URL.revokeObjectURL && URL) ||
13996 (window.webkitURL && webkitURL);
14001 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14003 this.selectorEl.on('change', this.onFileSelected, this);
14006 this.images.forEach(function(img) {
14009 this.images = false;
14011 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14017 onClick : function(e)
14019 e.preventDefault();
14021 this.selectorEl.dom.click();
14025 onFileSelected : function(e)
14027 e.preventDefault();
14029 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14033 Roo.each(this.selectorEl.dom.files, function(file){
14034 this.addFile(file);
14043 addFile : function(file)
14046 if(typeof(file) === 'string'){
14047 throw "Add file by name?"; // should not happen
14051 if(!file || !this.urlAPI){
14061 var url = _this.urlAPI.createObjectURL( file);
14064 id : Roo.bootstrap.CardUploader.ID--,
14065 is_uploaded : false,
14069 mimetype : file.type,
14077 * addCard - add an Attachment to the uploader
14078 * @param data - the data about the image to upload
14082 title : "Title of file",
14083 is_uploaded : false,
14084 src : "http://.....",
14085 srcfile : { the File upload object },
14086 mimetype : file.type,
14089 .. any other data...
14095 addCard : function (data)
14097 // hidden input element?
14098 // if the file is not an image...
14099 //then we need to use something other that and header_image
14104 xns : Roo.bootstrap,
14105 xtype : 'CardFooter',
14108 xns : Roo.bootstrap,
14114 xns : Roo.bootstrap,
14116 html : String.format("<small>{0}</small>", data.title),
14117 cls : 'col-10 text-left',
14122 click : function() {
14124 t.fireEvent( "download", t, data );
14130 xns : Roo.bootstrap,
14132 style: 'max-height: 28px; ',
14138 click : function() {
14139 t.removeCard(data.id)
14151 var cn = this.addxtype(
14154 xns : Roo.bootstrap,
14157 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14158 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14159 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14164 initEvents : function() {
14165 Roo.bootstrap.Card.prototype.initEvents.call(this);
14167 this.imgEl = this.el.select('.card-img-top').first();
14169 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14170 this.imgEl.set({ 'pointer' : 'cursor' });
14173 this.getCardFooter().addClass('p-1');
14180 // dont' really need ot update items.
14181 // this.items.push(cn);
14182 this.fileCollection.add(cn);
14184 if (!data.srcfile) {
14185 this.updateInput();
14190 var reader = new FileReader();
14191 reader.addEventListener("load", function() {
14192 data.srcdata = reader.result;
14195 reader.readAsDataURL(data.srcfile);
14200 removeCard : function(id)
14203 var card = this.fileCollection.get(id);
14204 card.data.is_deleted = 1;
14205 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14206 //this.fileCollection.remove(card);
14207 //this.items = this.items.filter(function(e) { return e != card });
14208 // dont' really need ot update items.
14209 card.el.dom.parentNode.removeChild(card.el.dom);
14210 this.updateInput();
14216 this.fileCollection.each(function(card) {
14217 if (card.el.dom && card.el.dom.parentNode) {
14218 card.el.dom.parentNode.removeChild(card.el.dom);
14221 this.fileCollection.clear();
14222 this.updateInput();
14225 updateInput : function()
14228 this.fileCollection.each(function(e) {
14232 this.inputEl().dom.value = JSON.stringify(data);
14242 Roo.bootstrap.CardUploader.ID = -1;/*
14244 * Ext JS Library 1.1.1
14245 * Copyright(c) 2006-2007, Ext JS, LLC.
14247 * Originally Released Under LGPL - original licence link has changed is not relivant.
14250 * <script type="text/javascript">
14255 * @class Roo.data.SortTypes
14257 * Defines the default sorting (casting?) comparison functions used when sorting data.
14259 Roo.data.SortTypes = {
14261 * Default sort that does nothing
14262 * @param {Mixed} s The value being converted
14263 * @return {Mixed} The comparison value
14265 none : function(s){
14270 * The regular expression used to strip tags
14274 stripTagsRE : /<\/?[^>]+>/gi,
14277 * Strips all HTML tags to sort on text only
14278 * @param {Mixed} s The value being converted
14279 * @return {String} The comparison value
14281 asText : function(s){
14282 return String(s).replace(this.stripTagsRE, "");
14286 * Strips all HTML tags to sort on text only - Case insensitive
14287 * @param {Mixed} s The value being converted
14288 * @return {String} The comparison value
14290 asUCText : function(s){
14291 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14295 * Case insensitive string
14296 * @param {Mixed} s The value being converted
14297 * @return {String} The comparison value
14299 asUCString : function(s) {
14300 return String(s).toUpperCase();
14305 * @param {Mixed} s The value being converted
14306 * @return {Number} The comparison value
14308 asDate : function(s) {
14312 if(s instanceof Date){
14313 return s.getTime();
14315 return Date.parse(String(s));
14320 * @param {Mixed} s The value being converted
14321 * @return {Float} The comparison value
14323 asFloat : function(s) {
14324 var val = parseFloat(String(s).replace(/,/g, ""));
14333 * @param {Mixed} s The value being converted
14334 * @return {Number} The comparison value
14336 asInt : function(s) {
14337 var val = parseInt(String(s).replace(/,/g, ""));
14345 * Ext JS Library 1.1.1
14346 * Copyright(c) 2006-2007, Ext JS, LLC.
14348 * Originally Released Under LGPL - original licence link has changed is not relivant.
14351 * <script type="text/javascript">
14355 * @class Roo.data.Record
14356 * Instances of this class encapsulate both record <em>definition</em> information, and record
14357 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14358 * to access Records cached in an {@link Roo.data.Store} object.<br>
14360 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14361 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14364 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14366 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14367 * {@link #create}. The parameters are the same.
14368 * @param {Array} data An associative Array of data values keyed by the field name.
14369 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14370 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14371 * not specified an integer id is generated.
14373 Roo.data.Record = function(data, id){
14374 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14379 * Generate a constructor for a specific record layout.
14380 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14381 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14382 * Each field definition object may contain the following properties: <ul>
14383 * <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,
14384 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14385 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14386 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14387 * is being used, then this is a string containing the javascript expression to reference the data relative to
14388 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14389 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14390 * this may be omitted.</p></li>
14391 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14392 * <ul><li>auto (Default, implies no conversion)</li>
14397 * <li>date</li></ul></p></li>
14398 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14399 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14400 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14401 * by the Reader into an object that will be stored in the Record. It is passed the
14402 * following parameters:<ul>
14403 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14405 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14407 * <br>usage:<br><pre><code>
14408 var TopicRecord = Roo.data.Record.create(
14409 {name: 'title', mapping: 'topic_title'},
14410 {name: 'author', mapping: 'username'},
14411 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14412 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14413 {name: 'lastPoster', mapping: 'user2'},
14414 {name: 'excerpt', mapping: 'post_text'}
14417 var myNewRecord = new TopicRecord({
14418 title: 'Do my job please',
14421 lastPost: new Date(),
14422 lastPoster: 'Animal',
14423 excerpt: 'No way dude!'
14425 myStore.add(myNewRecord);
14430 Roo.data.Record.create = function(o){
14431 var f = function(){
14432 f.superclass.constructor.apply(this, arguments);
14434 Roo.extend(f, Roo.data.Record);
14435 var p = f.prototype;
14436 p.fields = new Roo.util.MixedCollection(false, function(field){
14439 for(var i = 0, len = o.length; i < len; i++){
14440 p.fields.add(new Roo.data.Field(o[i]));
14442 f.getField = function(name){
14443 return p.fields.get(name);
14448 Roo.data.Record.AUTO_ID = 1000;
14449 Roo.data.Record.EDIT = 'edit';
14450 Roo.data.Record.REJECT = 'reject';
14451 Roo.data.Record.COMMIT = 'commit';
14453 Roo.data.Record.prototype = {
14455 * Readonly flag - true if this record has been modified.
14464 join : function(store){
14465 this.store = store;
14469 * Set the named field to the specified value.
14470 * @param {String} name The name of the field to set.
14471 * @param {Object} value The value to set the field to.
14473 set : function(name, value){
14474 if(this.data[name] == value){
14478 if(!this.modified){
14479 this.modified = {};
14481 if(typeof this.modified[name] == 'undefined'){
14482 this.modified[name] = this.data[name];
14484 this.data[name] = value;
14485 if(!this.editing && this.store){
14486 this.store.afterEdit(this);
14491 * Get the value of the named field.
14492 * @param {String} name The name of the field to get the value of.
14493 * @return {Object} The value of the field.
14495 get : function(name){
14496 return this.data[name];
14500 beginEdit : function(){
14501 this.editing = true;
14502 this.modified = {};
14506 cancelEdit : function(){
14507 this.editing = false;
14508 delete this.modified;
14512 endEdit : function(){
14513 this.editing = false;
14514 if(this.dirty && this.store){
14515 this.store.afterEdit(this);
14520 * Usually called by the {@link Roo.data.Store} which owns the Record.
14521 * Rejects all changes made to the Record since either creation, or the last commit operation.
14522 * Modified fields are reverted to their original values.
14524 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14525 * of reject operations.
14527 reject : function(){
14528 var m = this.modified;
14530 if(typeof m[n] != "function"){
14531 this.data[n] = m[n];
14534 this.dirty = false;
14535 delete this.modified;
14536 this.editing = false;
14538 this.store.afterReject(this);
14543 * Usually called by the {@link Roo.data.Store} which owns the Record.
14544 * Commits all changes made to the Record since either creation, or the last commit operation.
14546 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14547 * of commit operations.
14549 commit : function(){
14550 this.dirty = false;
14551 delete this.modified;
14552 this.editing = false;
14554 this.store.afterCommit(this);
14559 hasError : function(){
14560 return this.error != null;
14564 clearError : function(){
14569 * Creates a copy of this record.
14570 * @param {String} id (optional) A new record id if you don't want to use this record's id
14573 copy : function(newId) {
14574 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14578 * Ext JS Library 1.1.1
14579 * Copyright(c) 2006-2007, Ext JS, LLC.
14581 * Originally Released Under LGPL - original licence link has changed is not relivant.
14584 * <script type="text/javascript">
14590 * @class Roo.data.Store
14591 * @extends Roo.util.Observable
14592 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14593 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14595 * 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
14596 * has no knowledge of the format of the data returned by the Proxy.<br>
14598 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14599 * instances from the data object. These records are cached and made available through accessor functions.
14601 * Creates a new Store.
14602 * @param {Object} config A config object containing the objects needed for the Store to access data,
14603 * and read the data into Records.
14605 Roo.data.Store = function(config){
14606 this.data = new Roo.util.MixedCollection(false);
14607 this.data.getKey = function(o){
14610 this.baseParams = {};
14612 this.paramNames = {
14617 "multisort" : "_multisort"
14620 if(config && config.data){
14621 this.inlineData = config.data;
14622 delete config.data;
14625 Roo.apply(this, config);
14627 if(this.reader){ // reader passed
14628 this.reader = Roo.factory(this.reader, Roo.data);
14629 this.reader.xmodule = this.xmodule || false;
14630 if(!this.recordType){
14631 this.recordType = this.reader.recordType;
14633 if(this.reader.onMetaChange){
14634 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14638 if(this.recordType){
14639 this.fields = this.recordType.prototype.fields;
14641 this.modified = [];
14645 * @event datachanged
14646 * Fires when the data cache has changed, and a widget which is using this Store
14647 * as a Record cache should refresh its view.
14648 * @param {Store} this
14650 datachanged : true,
14652 * @event metachange
14653 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14654 * @param {Store} this
14655 * @param {Object} meta The JSON metadata
14660 * Fires when Records have been added to the Store
14661 * @param {Store} this
14662 * @param {Roo.data.Record[]} records The array of Records added
14663 * @param {Number} index The index at which the record(s) were added
14668 * Fires when a Record has been removed from the Store
14669 * @param {Store} this
14670 * @param {Roo.data.Record} record The Record that was removed
14671 * @param {Number} index The index at which the record was removed
14676 * Fires when a Record has been updated
14677 * @param {Store} this
14678 * @param {Roo.data.Record} record The Record that was updated
14679 * @param {String} operation The update operation being performed. Value may be one of:
14681 Roo.data.Record.EDIT
14682 Roo.data.Record.REJECT
14683 Roo.data.Record.COMMIT
14689 * Fires when the data cache has been cleared.
14690 * @param {Store} this
14694 * @event beforeload
14695 * Fires before a request is made for a new data object. If the beforeload handler returns false
14696 * the load action will be canceled.
14697 * @param {Store} this
14698 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14702 * @event beforeloadadd
14703 * Fires after a new set of Records has been loaded.
14704 * @param {Store} this
14705 * @param {Roo.data.Record[]} records The Records that were loaded
14706 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14708 beforeloadadd : true,
14711 * Fires after a new set of Records has been loaded, before they are added to the store.
14712 * @param {Store} this
14713 * @param {Roo.data.Record[]} records The Records that were loaded
14714 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14715 * @params {Object} return from reader
14719 * @event loadexception
14720 * Fires if an exception occurs in the Proxy during loading.
14721 * Called with the signature of the Proxy's "loadexception" event.
14722 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14725 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14726 * @param {Object} load options
14727 * @param {Object} jsonData from your request (normally this contains the Exception)
14729 loadexception : true
14733 this.proxy = Roo.factory(this.proxy, Roo.data);
14734 this.proxy.xmodule = this.xmodule || false;
14735 this.relayEvents(this.proxy, ["loadexception"]);
14737 this.sortToggle = {};
14738 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14740 Roo.data.Store.superclass.constructor.call(this);
14742 if(this.inlineData){
14743 this.loadData(this.inlineData);
14744 delete this.inlineData;
14748 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14750 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14751 * without a remote query - used by combo/forms at present.
14755 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14758 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14761 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14762 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14765 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14766 * on any HTTP request
14769 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14772 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14776 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14777 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14779 remoteSort : false,
14782 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14783 * loaded or when a record is removed. (defaults to false).
14785 pruneModifiedRecords : false,
14788 lastOptions : null,
14791 * Add Records to the Store and fires the add event.
14792 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14794 add : function(records){
14795 records = [].concat(records);
14796 for(var i = 0, len = records.length; i < len; i++){
14797 records[i].join(this);
14799 var index = this.data.length;
14800 this.data.addAll(records);
14801 this.fireEvent("add", this, records, index);
14805 * Remove a Record from the Store and fires the remove event.
14806 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14808 remove : function(record){
14809 var index = this.data.indexOf(record);
14810 this.data.removeAt(index);
14812 if(this.pruneModifiedRecords){
14813 this.modified.remove(record);
14815 this.fireEvent("remove", this, record, index);
14819 * Remove all Records from the Store and fires the clear event.
14821 removeAll : function(){
14823 if(this.pruneModifiedRecords){
14824 this.modified = [];
14826 this.fireEvent("clear", this);
14830 * Inserts Records to the Store at the given index and fires the add event.
14831 * @param {Number} index The start index at which to insert the passed Records.
14832 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14834 insert : function(index, records){
14835 records = [].concat(records);
14836 for(var i = 0, len = records.length; i < len; i++){
14837 this.data.insert(index, records[i]);
14838 records[i].join(this);
14840 this.fireEvent("add", this, records, index);
14844 * Get the index within the cache of the passed Record.
14845 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14846 * @return {Number} The index of the passed Record. Returns -1 if not found.
14848 indexOf : function(record){
14849 return this.data.indexOf(record);
14853 * Get the index within the cache of the Record with the passed id.
14854 * @param {String} id The id of the Record to find.
14855 * @return {Number} The index of the Record. Returns -1 if not found.
14857 indexOfId : function(id){
14858 return this.data.indexOfKey(id);
14862 * Get the Record with the specified id.
14863 * @param {String} id The id of the Record to find.
14864 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14866 getById : function(id){
14867 return this.data.key(id);
14871 * Get the Record at the specified index.
14872 * @param {Number} index The index of the Record to find.
14873 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14875 getAt : function(index){
14876 return this.data.itemAt(index);
14880 * Returns a range of Records between specified indices.
14881 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14882 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14883 * @return {Roo.data.Record[]} An array of Records
14885 getRange : function(start, end){
14886 return this.data.getRange(start, end);
14890 storeOptions : function(o){
14891 o = Roo.apply({}, o);
14894 this.lastOptions = o;
14898 * Loads the Record cache from the configured Proxy using the configured Reader.
14900 * If using remote paging, then the first load call must specify the <em>start</em>
14901 * and <em>limit</em> properties in the options.params property to establish the initial
14902 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14904 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14905 * and this call will return before the new data has been loaded. Perform any post-processing
14906 * in a callback function, or in a "load" event handler.</strong>
14908 * @param {Object} options An object containing properties which control loading options:<ul>
14909 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14910 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14911 * passed the following arguments:<ul>
14912 * <li>r : Roo.data.Record[]</li>
14913 * <li>options: Options object from the load call</li>
14914 * <li>success: Boolean success indicator</li></ul></li>
14915 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14916 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14919 load : function(options){
14920 options = options || {};
14921 if(this.fireEvent("beforeload", this, options) !== false){
14922 this.storeOptions(options);
14923 var p = Roo.apply(options.params || {}, this.baseParams);
14924 // if meta was not loaded from remote source.. try requesting it.
14925 if (!this.reader.metaFromRemote) {
14926 p._requestMeta = 1;
14928 if(this.sortInfo && this.remoteSort){
14929 var pn = this.paramNames;
14930 p[pn["sort"]] = this.sortInfo.field;
14931 p[pn["dir"]] = this.sortInfo.direction;
14933 if (this.multiSort) {
14934 var pn = this.paramNames;
14935 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14938 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14943 * Reloads the Record cache from the configured Proxy using the configured Reader and
14944 * the options from the last load operation performed.
14945 * @param {Object} options (optional) An object containing properties which may override the options
14946 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14947 * the most recently used options are reused).
14949 reload : function(options){
14950 this.load(Roo.applyIf(options||{}, this.lastOptions));
14954 // Called as a callback by the Reader during a load operation.
14955 loadRecords : function(o, options, success){
14956 if(!o || success === false){
14957 if(success !== false){
14958 this.fireEvent("load", this, [], options, o);
14960 if(options.callback){
14961 options.callback.call(options.scope || this, [], options, false);
14965 // if data returned failure - throw an exception.
14966 if (o.success === false) {
14967 // show a message if no listener is registered.
14968 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14969 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14971 // loadmask wil be hooked into this..
14972 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14975 var r = o.records, t = o.totalRecords || r.length;
14977 this.fireEvent("beforeloadadd", this, r, options, o);
14979 if(!options || options.add !== true){
14980 if(this.pruneModifiedRecords){
14981 this.modified = [];
14983 for(var i = 0, len = r.length; i < len; i++){
14987 this.data = this.snapshot;
14988 delete this.snapshot;
14991 this.data.addAll(r);
14992 this.totalLength = t;
14994 this.fireEvent("datachanged", this);
14996 this.totalLength = Math.max(t, this.data.length+r.length);
15000 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15002 var e = new Roo.data.Record({});
15004 e.set(this.parent.displayField, this.parent.emptyTitle);
15005 e.set(this.parent.valueField, '');
15010 this.fireEvent("load", this, r, options, o);
15011 if(options.callback){
15012 options.callback.call(options.scope || this, r, options, true);
15018 * Loads data from a passed data block. A Reader which understands the format of the data
15019 * must have been configured in the constructor.
15020 * @param {Object} data The data block from which to read the Records. The format of the data expected
15021 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15022 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15024 loadData : function(o, append){
15025 var r = this.reader.readRecords(o);
15026 this.loadRecords(r, {add: append}, true);
15030 * using 'cn' the nested child reader read the child array into it's child stores.
15031 * @param {Object} rec The record with a 'children array
15033 loadDataFromChildren : function(rec)
15035 this.loadData(this.reader.toLoadData(rec));
15040 * Gets the number of cached records.
15042 * <em>If using paging, this may not be the total size of the dataset. If the data object
15043 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15044 * the data set size</em>
15046 getCount : function(){
15047 return this.data.length || 0;
15051 * Gets the total number of records in the dataset as returned by the server.
15053 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15054 * the dataset size</em>
15056 getTotalCount : function(){
15057 return this.totalLength || 0;
15061 * Returns the sort state of the Store as an object with two properties:
15063 field {String} The name of the field by which the Records are sorted
15064 direction {String} The sort order, "ASC" or "DESC"
15067 getSortState : function(){
15068 return this.sortInfo;
15072 applySort : function(){
15073 if(this.sortInfo && !this.remoteSort){
15074 var s = this.sortInfo, f = s.field;
15075 var st = this.fields.get(f).sortType;
15076 var fn = function(r1, r2){
15077 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15078 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15080 this.data.sort(s.direction, fn);
15081 if(this.snapshot && this.snapshot != this.data){
15082 this.snapshot.sort(s.direction, fn);
15088 * Sets the default sort column and order to be used by the next load operation.
15089 * @param {String} fieldName The name of the field to sort by.
15090 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15092 setDefaultSort : function(field, dir){
15093 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15097 * Sort the Records.
15098 * If remote sorting is used, the sort is performed on the server, and the cache is
15099 * reloaded. If local sorting is used, the cache is sorted internally.
15100 * @param {String} fieldName The name of the field to sort by.
15101 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15103 sort : function(fieldName, dir){
15104 var f = this.fields.get(fieldName);
15106 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15108 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15109 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15114 this.sortToggle[f.name] = dir;
15115 this.sortInfo = {field: f.name, direction: dir};
15116 if(!this.remoteSort){
15118 this.fireEvent("datachanged", this);
15120 this.load(this.lastOptions);
15125 * Calls the specified function for each of the Records in the cache.
15126 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15127 * Returning <em>false</em> aborts and exits the iteration.
15128 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15130 each : function(fn, scope){
15131 this.data.each(fn, scope);
15135 * Gets all records modified since the last commit. Modified records are persisted across load operations
15136 * (e.g., during paging).
15137 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15139 getModifiedRecords : function(){
15140 return this.modified;
15144 createFilterFn : function(property, value, anyMatch){
15145 if(!value.exec){ // not a regex
15146 value = String(value);
15147 if(value.length == 0){
15150 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15152 return function(r){
15153 return value.test(r.data[property]);
15158 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15159 * @param {String} property A field on your records
15160 * @param {Number} start The record index to start at (defaults to 0)
15161 * @param {Number} end The last record index to include (defaults to length - 1)
15162 * @return {Number} The sum
15164 sum : function(property, start, end){
15165 var rs = this.data.items, v = 0;
15166 start = start || 0;
15167 end = (end || end === 0) ? end : rs.length-1;
15169 for(var i = start; i <= end; i++){
15170 v += (rs[i].data[property] || 0);
15176 * Filter the records by a specified property.
15177 * @param {String} field A field on your records
15178 * @param {String/RegExp} value Either a string that the field
15179 * should start with or a RegExp to test against the field
15180 * @param {Boolean} anyMatch True to match any part not just the beginning
15182 filter : function(property, value, anyMatch){
15183 var fn = this.createFilterFn(property, value, anyMatch);
15184 return fn ? this.filterBy(fn) : this.clearFilter();
15188 * Filter by a function. The specified function will be called with each
15189 * record in this data source. If the function returns true the record is included,
15190 * otherwise it is filtered.
15191 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15192 * @param {Object} scope (optional) The scope of the function (defaults to this)
15194 filterBy : function(fn, scope){
15195 this.snapshot = this.snapshot || this.data;
15196 this.data = this.queryBy(fn, scope||this);
15197 this.fireEvent("datachanged", this);
15201 * Query the records by a specified property.
15202 * @param {String} field A field on your records
15203 * @param {String/RegExp} value Either a string that the field
15204 * should start with or a RegExp to test against the field
15205 * @param {Boolean} anyMatch True to match any part not just the beginning
15206 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15208 query : function(property, value, anyMatch){
15209 var fn = this.createFilterFn(property, value, anyMatch);
15210 return fn ? this.queryBy(fn) : this.data.clone();
15214 * Query by a function. The specified function will be called with each
15215 * record in this data source. If the function returns true the record is included
15217 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15218 * @param {Object} scope (optional) The scope of the function (defaults to this)
15219 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15221 queryBy : function(fn, scope){
15222 var data = this.snapshot || this.data;
15223 return data.filterBy(fn, scope||this);
15227 * Collects unique values for a particular dataIndex from this store.
15228 * @param {String} dataIndex The property to collect
15229 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15230 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15231 * @return {Array} An array of the unique values
15233 collect : function(dataIndex, allowNull, bypassFilter){
15234 var d = (bypassFilter === true && this.snapshot) ?
15235 this.snapshot.items : this.data.items;
15236 var v, sv, r = [], l = {};
15237 for(var i = 0, len = d.length; i < len; i++){
15238 v = d[i].data[dataIndex];
15240 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15249 * Revert to a view of the Record cache with no filtering applied.
15250 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15252 clearFilter : function(suppressEvent){
15253 if(this.snapshot && this.snapshot != this.data){
15254 this.data = this.snapshot;
15255 delete this.snapshot;
15256 if(suppressEvent !== true){
15257 this.fireEvent("datachanged", this);
15263 afterEdit : function(record){
15264 if(this.modified.indexOf(record) == -1){
15265 this.modified.push(record);
15267 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15271 afterReject : function(record){
15272 this.modified.remove(record);
15273 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15277 afterCommit : function(record){
15278 this.modified.remove(record);
15279 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15283 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15284 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15286 commitChanges : function(){
15287 var m = this.modified.slice(0);
15288 this.modified = [];
15289 for(var i = 0, len = m.length; i < len; i++){
15295 * Cancel outstanding changes on all changed records.
15297 rejectChanges : function(){
15298 var m = this.modified.slice(0);
15299 this.modified = [];
15300 for(var i = 0, len = m.length; i < len; i++){
15305 onMetaChange : function(meta, rtype, o){
15306 this.recordType = rtype;
15307 this.fields = rtype.prototype.fields;
15308 delete this.snapshot;
15309 this.sortInfo = meta.sortInfo || this.sortInfo;
15310 this.modified = [];
15311 this.fireEvent('metachange', this, this.reader.meta);
15314 moveIndex : function(data, type)
15316 var index = this.indexOf(data);
15318 var newIndex = index + type;
15322 this.insert(newIndex, data);
15327 * Ext JS Library 1.1.1
15328 * Copyright(c) 2006-2007, Ext JS, LLC.
15330 * Originally Released Under LGPL - original licence link has changed is not relivant.
15333 * <script type="text/javascript">
15337 * @class Roo.data.SimpleStore
15338 * @extends Roo.data.Store
15339 * Small helper class to make creating Stores from Array data easier.
15340 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15341 * @cfg {Array} fields An array of field definition objects, or field name strings.
15342 * @cfg {Object} an existing reader (eg. copied from another store)
15343 * @cfg {Array} data The multi-dimensional array of data
15345 * @param {Object} config
15347 Roo.data.SimpleStore = function(config)
15349 Roo.data.SimpleStore.superclass.constructor.call(this, {
15351 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15354 Roo.data.Record.create(config.fields)
15356 proxy : new Roo.data.MemoryProxy(config.data)
15360 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15362 * Ext JS Library 1.1.1
15363 * Copyright(c) 2006-2007, Ext JS, LLC.
15365 * Originally Released Under LGPL - original licence link has changed is not relivant.
15368 * <script type="text/javascript">
15373 * @extends Roo.data.Store
15374 * @class Roo.data.JsonStore
15375 * Small helper class to make creating Stores for JSON data easier. <br/>
15377 var store = new Roo.data.JsonStore({
15378 url: 'get-images.php',
15380 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15383 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15384 * JsonReader and HttpProxy (unless inline data is provided).</b>
15385 * @cfg {Array} fields An array of field definition objects, or field name strings.
15387 * @param {Object} config
15389 Roo.data.JsonStore = function(c){
15390 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15391 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15392 reader: new Roo.data.JsonReader(c, c.fields)
15395 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15397 * Ext JS Library 1.1.1
15398 * Copyright(c) 2006-2007, Ext JS, LLC.
15400 * Originally Released Under LGPL - original licence link has changed is not relivant.
15403 * <script type="text/javascript">
15407 Roo.data.Field = function(config){
15408 if(typeof config == "string"){
15409 config = {name: config};
15411 Roo.apply(this, config);
15414 this.type = "auto";
15417 var st = Roo.data.SortTypes;
15418 // named sortTypes are supported, here we look them up
15419 if(typeof this.sortType == "string"){
15420 this.sortType = st[this.sortType];
15423 // set default sortType for strings and dates
15424 if(!this.sortType){
15427 this.sortType = st.asUCString;
15430 this.sortType = st.asDate;
15433 this.sortType = st.none;
15438 var stripRe = /[\$,%]/g;
15440 // prebuilt conversion function for this field, instead of
15441 // switching every time we're reading a value
15443 var cv, dateFormat = this.dateFormat;
15448 cv = function(v){ return v; };
15451 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15455 return v !== undefined && v !== null && v !== '' ?
15456 parseInt(String(v).replace(stripRe, ""), 10) : '';
15461 return v !== undefined && v !== null && v !== '' ?
15462 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15467 cv = function(v){ return v === true || v === "true" || v == 1; };
15474 if(v instanceof Date){
15478 if(dateFormat == "timestamp"){
15479 return new Date(v*1000);
15481 return Date.parseDate(v, dateFormat);
15483 var parsed = Date.parse(v);
15484 return parsed ? new Date(parsed) : null;
15493 Roo.data.Field.prototype = {
15501 * Ext JS Library 1.1.1
15502 * Copyright(c) 2006-2007, Ext JS, LLC.
15504 * Originally Released Under LGPL - original licence link has changed is not relivant.
15507 * <script type="text/javascript">
15510 // Base class for reading structured data from a data source. This class is intended to be
15511 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15514 * @class Roo.data.DataReader
15515 * Base class for reading structured data from a data source. This class is intended to be
15516 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15519 Roo.data.DataReader = function(meta, recordType){
15523 this.recordType = recordType instanceof Array ?
15524 Roo.data.Record.create(recordType) : recordType;
15527 Roo.data.DataReader.prototype = {
15530 readerType : 'Data',
15532 * Create an empty record
15533 * @param {Object} data (optional) - overlay some values
15534 * @return {Roo.data.Record} record created.
15536 newRow : function(d) {
15538 this.recordType.prototype.fields.each(function(c) {
15540 case 'int' : da[c.name] = 0; break;
15541 case 'date' : da[c.name] = new Date(); break;
15542 case 'float' : da[c.name] = 0.0; break;
15543 case 'boolean' : da[c.name] = false; break;
15544 default : da[c.name] = ""; break;
15548 return new this.recordType(Roo.apply(da, d));
15554 * Ext JS Library 1.1.1
15555 * Copyright(c) 2006-2007, Ext JS, LLC.
15557 * Originally Released Under LGPL - original licence link has changed is not relivant.
15560 * <script type="text/javascript">
15564 * @class Roo.data.DataProxy
15565 * @extends Roo.data.Observable
15566 * This class is an abstract base class for implementations which provide retrieval of
15567 * unformatted data objects.<br>
15569 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15570 * (of the appropriate type which knows how to parse the data object) to provide a block of
15571 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15573 * Custom implementations must implement the load method as described in
15574 * {@link Roo.data.HttpProxy#load}.
15576 Roo.data.DataProxy = function(){
15579 * @event beforeload
15580 * Fires before a network request is made to retrieve a data object.
15581 * @param {Object} This DataProxy object.
15582 * @param {Object} params The params parameter to the load function.
15587 * Fires before the load method's callback is called.
15588 * @param {Object} This DataProxy object.
15589 * @param {Object} o The data object.
15590 * @param {Object} arg The callback argument object passed to the load function.
15594 * @event loadexception
15595 * Fires if an Exception occurs during data retrieval.
15596 * @param {Object} This DataProxy object.
15597 * @param {Object} o The data object.
15598 * @param {Object} arg The callback argument object passed to the load function.
15599 * @param {Object} e The Exception.
15601 loadexception : true
15603 Roo.data.DataProxy.superclass.constructor.call(this);
15606 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15609 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15613 * Ext JS Library 1.1.1
15614 * Copyright(c) 2006-2007, Ext JS, LLC.
15616 * Originally Released Under LGPL - original licence link has changed is not relivant.
15619 * <script type="text/javascript">
15622 * @class Roo.data.MemoryProxy
15623 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15624 * to the Reader when its load method is called.
15626 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15628 Roo.data.MemoryProxy = function(data){
15632 Roo.data.MemoryProxy.superclass.constructor.call(this);
15636 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15639 * Load data from the requested source (in this case an in-memory
15640 * data object passed to the constructor), read the data object into
15641 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15642 * process that block using the passed callback.
15643 * @param {Object} params This parameter is not used by the MemoryProxy class.
15644 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15645 * object into a block of Roo.data.Records.
15646 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15647 * The function must be passed <ul>
15648 * <li>The Record block object</li>
15649 * <li>The "arg" argument from the load function</li>
15650 * <li>A boolean success indicator</li>
15652 * @param {Object} scope The scope in which to call the callback
15653 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15655 load : function(params, reader, callback, scope, arg){
15656 params = params || {};
15659 result = reader.readRecords(params.data ? params.data :this.data);
15661 this.fireEvent("loadexception", this, arg, null, e);
15662 callback.call(scope, null, arg, false);
15665 callback.call(scope, result, arg, true);
15669 update : function(params, records){
15674 * Ext JS Library 1.1.1
15675 * Copyright(c) 2006-2007, Ext JS, LLC.
15677 * Originally Released Under LGPL - original licence link has changed is not relivant.
15680 * <script type="text/javascript">
15683 * @class Roo.data.HttpProxy
15684 * @extends Roo.data.DataProxy
15685 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15686 * configured to reference a certain URL.<br><br>
15688 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15689 * from which the running page was served.<br><br>
15691 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15693 * Be aware that to enable the browser to parse an XML document, the server must set
15694 * the Content-Type header in the HTTP response to "text/xml".
15696 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15697 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15698 * will be used to make the request.
15700 Roo.data.HttpProxy = function(conn){
15701 Roo.data.HttpProxy.superclass.constructor.call(this);
15702 // is conn a conn config or a real conn?
15704 this.useAjax = !conn || !conn.events;
15708 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15709 // thse are take from connection...
15712 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15715 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15716 * extra parameters to each request made by this object. (defaults to undefined)
15719 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15720 * to each request made by this object. (defaults to undefined)
15723 * @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)
15726 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15729 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15735 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15739 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15740 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15741 * a finer-grained basis than the DataProxy events.
15743 getConnection : function(){
15744 return this.useAjax ? Roo.Ajax : this.conn;
15748 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15749 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15750 * process that block using the passed callback.
15751 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15752 * for the request to the remote server.
15753 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15754 * object into a block of Roo.data.Records.
15755 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15756 * The function must be passed <ul>
15757 * <li>The Record block object</li>
15758 * <li>The "arg" argument from the load function</li>
15759 * <li>A boolean success indicator</li>
15761 * @param {Object} scope The scope in which to call the callback
15762 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15764 load : function(params, reader, callback, scope, arg){
15765 if(this.fireEvent("beforeload", this, params) !== false){
15767 params : params || {},
15769 callback : callback,
15774 callback : this.loadResponse,
15778 Roo.applyIf(o, this.conn);
15779 if(this.activeRequest){
15780 Roo.Ajax.abort(this.activeRequest);
15782 this.activeRequest = Roo.Ajax.request(o);
15784 this.conn.request(o);
15787 callback.call(scope||this, null, arg, false);
15792 loadResponse : function(o, success, response){
15793 delete this.activeRequest;
15795 this.fireEvent("loadexception", this, o, response);
15796 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15801 result = o.reader.read(response);
15803 this.fireEvent("loadexception", this, o, response, e);
15804 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15808 this.fireEvent("load", this, o, o.request.arg);
15809 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15813 update : function(dataSet){
15818 updateResponse : function(dataSet){
15823 * Ext JS Library 1.1.1
15824 * Copyright(c) 2006-2007, Ext JS, LLC.
15826 * Originally Released Under LGPL - original licence link has changed is not relivant.
15829 * <script type="text/javascript">
15833 * @class Roo.data.ScriptTagProxy
15834 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15835 * other than the originating domain of the running page.<br><br>
15837 * <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
15838 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15840 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15841 * source code that is used as the source inside a <script> tag.<br><br>
15843 * In order for the browser to process the returned data, the server must wrap the data object
15844 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15845 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15846 * depending on whether the callback name was passed:
15849 boolean scriptTag = false;
15850 String cb = request.getParameter("callback");
15853 response.setContentType("text/javascript");
15855 response.setContentType("application/x-json");
15857 Writer out = response.getWriter();
15859 out.write(cb + "(");
15861 out.print(dataBlock.toJsonString());
15868 * @param {Object} config A configuration object.
15870 Roo.data.ScriptTagProxy = function(config){
15871 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15872 Roo.apply(this, config);
15873 this.head = document.getElementsByTagName("head")[0];
15876 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15878 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15880 * @cfg {String} url The URL from which to request the data object.
15883 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15887 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15888 * the server the name of the callback function set up by the load call to process the returned data object.
15889 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15890 * javascript output which calls this named function passing the data object as its only parameter.
15892 callbackParam : "callback",
15894 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15895 * name to the request.
15900 * Load data from the configured URL, read the data object into
15901 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15902 * process that block using the passed callback.
15903 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15904 * for the request to the remote server.
15905 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15906 * object into a block of Roo.data.Records.
15907 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15908 * The function must be passed <ul>
15909 * <li>The Record block object</li>
15910 * <li>The "arg" argument from the load function</li>
15911 * <li>A boolean success indicator</li>
15913 * @param {Object} scope The scope in which to call the callback
15914 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15916 load : function(params, reader, callback, scope, arg){
15917 if(this.fireEvent("beforeload", this, params) !== false){
15919 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15921 var url = this.url;
15922 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15924 url += "&_dc=" + (new Date().getTime());
15926 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15929 cb : "stcCallback"+transId,
15930 scriptId : "stcScript"+transId,
15934 callback : callback,
15940 window[trans.cb] = function(o){
15941 conn.handleResponse(o, trans);
15944 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15946 if(this.autoAbort !== false){
15950 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15952 var script = document.createElement("script");
15953 script.setAttribute("src", url);
15954 script.setAttribute("type", "text/javascript");
15955 script.setAttribute("id", trans.scriptId);
15956 this.head.appendChild(script);
15958 this.trans = trans;
15960 callback.call(scope||this, null, arg, false);
15965 isLoading : function(){
15966 return this.trans ? true : false;
15970 * Abort the current server request.
15972 abort : function(){
15973 if(this.isLoading()){
15974 this.destroyTrans(this.trans);
15979 destroyTrans : function(trans, isLoaded){
15980 this.head.removeChild(document.getElementById(trans.scriptId));
15981 clearTimeout(trans.timeoutId);
15983 window[trans.cb] = undefined;
15985 delete window[trans.cb];
15988 // if hasn't been loaded, wait for load to remove it to prevent script error
15989 window[trans.cb] = function(){
15990 window[trans.cb] = undefined;
15992 delete window[trans.cb];
15999 handleResponse : function(o, trans){
16000 this.trans = false;
16001 this.destroyTrans(trans, true);
16004 result = trans.reader.readRecords(o);
16006 this.fireEvent("loadexception", this, o, trans.arg, e);
16007 trans.callback.call(trans.scope||window, null, trans.arg, false);
16010 this.fireEvent("load", this, o, trans.arg);
16011 trans.callback.call(trans.scope||window, result, trans.arg, true);
16015 handleFailure : function(trans){
16016 this.trans = false;
16017 this.destroyTrans(trans, false);
16018 this.fireEvent("loadexception", this, null, trans.arg);
16019 trans.callback.call(trans.scope||window, null, trans.arg, false);
16023 * Ext JS Library 1.1.1
16024 * Copyright(c) 2006-2007, Ext JS, LLC.
16026 * Originally Released Under LGPL - original licence link has changed is not relivant.
16029 * <script type="text/javascript">
16033 * @class Roo.data.JsonReader
16034 * @extends Roo.data.DataReader
16035 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16036 * based on mappings in a provided Roo.data.Record constructor.
16038 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16039 * in the reply previously.
16044 var RecordDef = Roo.data.Record.create([
16045 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16046 {name: 'occupation'} // This field will use "occupation" as the mapping.
16048 var myReader = new Roo.data.JsonReader({
16049 totalProperty: "results", // The property which contains the total dataset size (optional)
16050 root: "rows", // The property which contains an Array of row objects
16051 id: "id" // The property within each row object that provides an ID for the record (optional)
16055 * This would consume a JSON file like this:
16057 { 'results': 2, 'rows': [
16058 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16059 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16062 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16063 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16064 * paged from the remote server.
16065 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16066 * @cfg {String} root name of the property which contains the Array of row objects.
16067 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16068 * @cfg {Array} fields Array of field definition objects
16070 * Create a new JsonReader
16071 * @param {Object} meta Metadata configuration options
16072 * @param {Object} recordType Either an Array of field definition objects,
16073 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16075 Roo.data.JsonReader = function(meta, recordType){
16078 // set some defaults:
16079 Roo.applyIf(meta, {
16080 totalProperty: 'total',
16081 successProperty : 'success',
16086 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16088 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16090 readerType : 'Json',
16093 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16094 * Used by Store query builder to append _requestMeta to params.
16097 metaFromRemote : false,
16099 * This method is only used by a DataProxy which has retrieved data from a remote server.
16100 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16101 * @return {Object} data A data block which is used by an Roo.data.Store object as
16102 * a cache of Roo.data.Records.
16104 read : function(response){
16105 var json = response.responseText;
16107 var o = /* eval:var:o */ eval("("+json+")");
16109 throw {message: "JsonReader.read: Json object not found"};
16115 this.metaFromRemote = true;
16116 this.meta = o.metaData;
16117 this.recordType = Roo.data.Record.create(o.metaData.fields);
16118 this.onMetaChange(this.meta, this.recordType, o);
16120 return this.readRecords(o);
16123 // private function a store will implement
16124 onMetaChange : function(meta, recordType, o){
16131 simpleAccess: function(obj, subsc) {
16138 getJsonAccessor: function(){
16140 return function(expr) {
16142 return(re.test(expr))
16143 ? new Function("obj", "return obj." + expr)
16148 return Roo.emptyFn;
16153 * Create a data block containing Roo.data.Records from an XML document.
16154 * @param {Object} o An object which contains an Array of row objects in the property specified
16155 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16156 * which contains the total size of the dataset.
16157 * @return {Object} data A data block which is used by an Roo.data.Store object as
16158 * a cache of Roo.data.Records.
16160 readRecords : function(o){
16162 * After any data loads, the raw JSON data is available for further custom processing.
16166 var s = this.meta, Record = this.recordType,
16167 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16169 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16171 if(s.totalProperty) {
16172 this.getTotal = this.getJsonAccessor(s.totalProperty);
16174 if(s.successProperty) {
16175 this.getSuccess = this.getJsonAccessor(s.successProperty);
16177 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16179 var g = this.getJsonAccessor(s.id);
16180 this.getId = function(rec) {
16182 return (r === undefined || r === "") ? null : r;
16185 this.getId = function(){return null;};
16188 for(var jj = 0; jj < fl; jj++){
16190 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16191 this.ef[jj] = this.getJsonAccessor(map);
16195 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16196 if(s.totalProperty){
16197 var vt = parseInt(this.getTotal(o), 10);
16202 if(s.successProperty){
16203 var vs = this.getSuccess(o);
16204 if(vs === false || vs === 'false'){
16209 for(var i = 0; i < c; i++){
16212 var id = this.getId(n);
16213 for(var j = 0; j < fl; j++){
16215 var v = this.ef[j](n);
16217 Roo.log('missing convert for ' + f.name);
16221 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16223 var record = new Record(values, id);
16225 records[i] = record;
16231 totalRecords : totalRecords
16234 // used when loading children.. @see loadDataFromChildren
16235 toLoadData: function(rec)
16237 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16238 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16239 return { data : data, total : data.length };
16244 * Ext JS Library 1.1.1
16245 * Copyright(c) 2006-2007, Ext JS, LLC.
16247 * Originally Released Under LGPL - original licence link has changed is not relivant.
16250 * <script type="text/javascript">
16254 * @class Roo.data.ArrayReader
16255 * @extends Roo.data.DataReader
16256 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16257 * Each element of that Array represents a row of data fields. The
16258 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16259 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16263 var RecordDef = Roo.data.Record.create([
16264 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16265 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16267 var myReader = new Roo.data.ArrayReader({
16268 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16272 * This would consume an Array like this:
16274 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16278 * Create a new JsonReader
16279 * @param {Object} meta Metadata configuration options.
16280 * @param {Object|Array} recordType Either an Array of field definition objects
16282 * @cfg {Array} fields Array of field definition objects
16283 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16284 * as specified to {@link Roo.data.Record#create},
16285 * or an {@link Roo.data.Record} object
16288 * created using {@link Roo.data.Record#create}.
16290 Roo.data.ArrayReader = function(meta, recordType)
16292 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16295 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16298 * Create a data block containing Roo.data.Records from an XML document.
16299 * @param {Object} o An Array of row objects which represents the dataset.
16300 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16301 * a cache of Roo.data.Records.
16303 readRecords : function(o)
16305 var sid = this.meta ? this.meta.id : null;
16306 var recordType = this.recordType, fields = recordType.prototype.fields;
16309 for(var i = 0; i < root.length; i++){
16312 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16313 for(var j = 0, jlen = fields.length; j < jlen; j++){
16314 var f = fields.items[j];
16315 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16316 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16318 values[f.name] = v;
16320 var record = new recordType(values, id);
16322 records[records.length] = record;
16326 totalRecords : records.length
16329 // used when loading children.. @see loadDataFromChildren
16330 toLoadData: function(rec)
16332 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16333 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16344 * @class Roo.bootstrap.ComboBox
16345 * @extends Roo.bootstrap.TriggerField
16346 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16347 * @cfg {Boolean} append (true|false) default false
16348 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16349 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16350 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16351 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16352 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16353 * @cfg {Boolean} animate default true
16354 * @cfg {Boolean} emptyResultText only for touch device
16355 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16356 * @cfg {String} emptyTitle default ''
16357 * @cfg {Number} width fixed with? experimental
16359 * Create a new ComboBox.
16360 * @param {Object} config Configuration options
16362 Roo.bootstrap.ComboBox = function(config){
16363 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16367 * Fires when the dropdown list is expanded
16368 * @param {Roo.bootstrap.ComboBox} combo This combo box
16373 * Fires when the dropdown list is collapsed
16374 * @param {Roo.bootstrap.ComboBox} combo This combo box
16378 * @event beforeselect
16379 * Fires before a list item is selected. Return false to cancel the selection.
16380 * @param {Roo.bootstrap.ComboBox} combo This combo box
16381 * @param {Roo.data.Record} record The data record returned from the underlying store
16382 * @param {Number} index The index of the selected item in the dropdown list
16384 'beforeselect' : true,
16387 * Fires when a list item is selected
16388 * @param {Roo.bootstrap.ComboBox} combo This combo box
16389 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16390 * @param {Number} index The index of the selected item in the dropdown list
16394 * @event beforequery
16395 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16396 * The event object passed has these properties:
16397 * @param {Roo.bootstrap.ComboBox} combo This combo box
16398 * @param {String} query The query
16399 * @param {Boolean} forceAll true to force "all" query
16400 * @param {Boolean} cancel true to cancel the query
16401 * @param {Object} e The query event object
16403 'beforequery': true,
16406 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16407 * @param {Roo.bootstrap.ComboBox} combo This combo box
16412 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16413 * @param {Roo.bootstrap.ComboBox} combo This combo box
16414 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16419 * Fires when the remove value from the combobox array
16420 * @param {Roo.bootstrap.ComboBox} combo This combo box
16424 * @event afterremove
16425 * Fires when the remove value from the combobox array
16426 * @param {Roo.bootstrap.ComboBox} combo This combo box
16428 'afterremove' : true,
16430 * @event specialfilter
16431 * Fires when specialfilter
16432 * @param {Roo.bootstrap.ComboBox} combo This combo box
16434 'specialfilter' : true,
16437 * Fires when tick the element
16438 * @param {Roo.bootstrap.ComboBox} combo This combo box
16442 * @event touchviewdisplay
16443 * Fires when touch view require special display (default is using displayField)
16444 * @param {Roo.bootstrap.ComboBox} combo This combo box
16445 * @param {Object} cfg set html .
16447 'touchviewdisplay' : true
16452 this.tickItems = [];
16454 this.selectedIndex = -1;
16455 if(this.mode == 'local'){
16456 if(config.queryDelay === undefined){
16457 this.queryDelay = 10;
16459 if(config.minChars === undefined){
16465 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16468 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16469 * rendering into an Roo.Editor, defaults to false)
16472 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16473 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16476 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16479 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16480 * the dropdown list (defaults to undefined, with no header element)
16484 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16488 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16490 listWidth: undefined,
16492 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16493 * mode = 'remote' or 'text' if mode = 'local')
16495 displayField: undefined,
16498 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16499 * mode = 'remote' or 'value' if mode = 'local').
16500 * Note: use of a valueField requires the user make a selection
16501 * in order for a value to be mapped.
16503 valueField: undefined,
16505 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16510 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16511 * field's data value (defaults to the underlying DOM element's name)
16513 hiddenName: undefined,
16515 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16519 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16521 selectedClass: 'active',
16524 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16528 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16529 * anchor positions (defaults to 'tl-bl')
16531 listAlign: 'tl-bl?',
16533 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16537 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16538 * query specified by the allQuery config option (defaults to 'query')
16540 triggerAction: 'query',
16542 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16543 * (defaults to 4, does not apply if editable = false)
16547 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16548 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16552 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16553 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16557 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16558 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16562 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16563 * when editable = true (defaults to false)
16565 selectOnFocus:false,
16567 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16569 queryParam: 'query',
16571 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16572 * when mode = 'remote' (defaults to 'Loading...')
16574 loadingText: 'Loading...',
16576 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16580 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16584 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16585 * traditional select (defaults to true)
16589 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16593 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16597 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16598 * listWidth has a higher value)
16602 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16603 * allow the user to set arbitrary text into the field (defaults to false)
16605 forceSelection:false,
16607 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16608 * if typeAhead = true (defaults to 250)
16610 typeAheadDelay : 250,
16612 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16613 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16615 valueNotFoundText : undefined,
16617 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16619 blockFocus : false,
16622 * @cfg {Boolean} disableClear Disable showing of clear button.
16624 disableClear : false,
16626 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16628 alwaysQuery : false,
16631 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16636 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16638 invalidClass : "has-warning",
16641 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16643 validClass : "has-success",
16646 * @cfg {Boolean} specialFilter (true|false) special filter default false
16648 specialFilter : false,
16651 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16653 mobileTouchView : true,
16656 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16658 useNativeIOS : false,
16661 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16663 mobile_restrict_height : false,
16665 ios_options : false,
16677 btnPosition : 'right',
16678 triggerList : true,
16679 showToggleBtn : true,
16681 emptyResultText: 'Empty',
16682 triggerText : 'Select',
16686 // element that contains real text value.. (when hidden is used..)
16688 getAutoCreate : function()
16693 * Render classic select for iso
16696 if(Roo.isIOS && this.useNativeIOS){
16697 cfg = this.getAutoCreateNativeIOS();
16705 if(Roo.isTouch && this.mobileTouchView){
16706 cfg = this.getAutoCreateTouchView();
16713 if(!this.tickable){
16714 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16719 * ComboBox with tickable selections
16722 var align = this.labelAlign || this.parentLabelAlign();
16725 cls : 'form-group roo-combobox-tickable' //input-group
16728 var btn_text_select = '';
16729 var btn_text_done = '';
16730 var btn_text_cancel = '';
16732 if (this.btn_text_show) {
16733 btn_text_select = 'Select';
16734 btn_text_done = 'Done';
16735 btn_text_cancel = 'Cancel';
16740 cls : 'tickable-buttons',
16745 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16746 //html : this.triggerText
16747 html: btn_text_select
16753 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16755 html: btn_text_done
16761 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16763 html: btn_text_cancel
16769 buttons.cn.unshift({
16771 cls: 'roo-select2-search-field-input'
16777 Roo.each(buttons.cn, function(c){
16779 c.cls += ' btn-' + _this.size;
16782 if (_this.disabled) {
16789 style : 'display: contents',
16794 cls: 'form-hidden-field'
16798 cls: 'roo-select2-choices',
16802 cls: 'roo-select2-search-field',
16813 cls: 'roo-select2-container input-group roo-select2-container-multi',
16819 // cls: 'typeahead typeahead-long dropdown-menu',
16820 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16825 if(this.hasFeedback && !this.allowBlank){
16829 cls: 'glyphicon form-control-feedback'
16832 combobox.cn.push(feedback);
16839 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16840 tooltip : 'This field is required'
16842 if (Roo.bootstrap.version == 4) {
16845 style : 'display:none'
16848 if (align ==='left' && this.fieldLabel.length) {
16850 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16857 cls : 'control-label col-form-label',
16858 html : this.fieldLabel
16870 var labelCfg = cfg.cn[1];
16871 var contentCfg = cfg.cn[2];
16874 if(this.indicatorpos == 'right'){
16880 cls : 'control-label col-form-label',
16884 html : this.fieldLabel
16900 labelCfg = cfg.cn[0];
16901 contentCfg = cfg.cn[1];
16905 if(this.labelWidth > 12){
16906 labelCfg.style = "width: " + this.labelWidth + 'px';
16908 if(this.width * 1 > 0){
16909 contentCfg.style = "width: " + this.width + 'px';
16911 if(this.labelWidth < 13 && this.labelmd == 0){
16912 this.labelmd = this.labelWidth;
16915 if(this.labellg > 0){
16916 labelCfg.cls += ' col-lg-' + this.labellg;
16917 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16920 if(this.labelmd > 0){
16921 labelCfg.cls += ' col-md-' + this.labelmd;
16922 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16925 if(this.labelsm > 0){
16926 labelCfg.cls += ' col-sm-' + this.labelsm;
16927 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16930 if(this.labelxs > 0){
16931 labelCfg.cls += ' col-xs-' + this.labelxs;
16932 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16936 } else if ( this.fieldLabel.length) {
16937 // Roo.log(" label");
16942 //cls : 'input-group-addon',
16943 html : this.fieldLabel
16948 if(this.indicatorpos == 'right'){
16952 //cls : 'input-group-addon',
16953 html : this.fieldLabel
16963 // Roo.log(" no label && no align");
16970 ['xs','sm','md','lg'].map(function(size){
16971 if (settings[size]) {
16972 cfg.cls += ' col-' + size + '-' + settings[size];
16980 _initEventsCalled : false,
16983 initEvents: function()
16985 if (this._initEventsCalled) { // as we call render... prevent looping...
16988 this._initEventsCalled = true;
16991 throw "can not find store for combo";
16994 this.indicator = this.indicatorEl();
16996 this.store = Roo.factory(this.store, Roo.data);
16997 this.store.parent = this;
16999 // if we are building from html. then this element is so complex, that we can not really
17000 // use the rendered HTML.
17001 // so we have to trash and replace the previous code.
17002 if (Roo.XComponent.build_from_html) {
17003 // remove this element....
17004 var e = this.el.dom, k=0;
17005 while (e ) { e = e.previousSibling; ++k;}
17010 this.rendered = false;
17012 this.render(this.parent().getChildContainer(true), k);
17015 if(Roo.isIOS && this.useNativeIOS){
17016 this.initIOSView();
17024 if(Roo.isTouch && this.mobileTouchView){
17025 this.initTouchView();
17030 this.initTickableEvents();
17034 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17036 if(this.hiddenName){
17038 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17040 this.hiddenField.dom.value =
17041 this.hiddenValue !== undefined ? this.hiddenValue :
17042 this.value !== undefined ? this.value : '';
17044 // prevent input submission
17045 this.el.dom.removeAttribute('name');
17046 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17051 // this.el.dom.setAttribute('autocomplete', 'off');
17054 var cls = 'x-combo-list';
17056 //this.list = new Roo.Layer({
17057 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17063 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17064 _this.list.setWidth(lw);
17067 this.list.on('mouseover', this.onViewOver, this);
17068 this.list.on('mousemove', this.onViewMove, this);
17069 this.list.on('scroll', this.onViewScroll, this);
17072 this.list.swallowEvent('mousewheel');
17073 this.assetHeight = 0;
17076 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17077 this.assetHeight += this.header.getHeight();
17080 this.innerList = this.list.createChild({cls:cls+'-inner'});
17081 this.innerList.on('mouseover', this.onViewOver, this);
17082 this.innerList.on('mousemove', this.onViewMove, this);
17083 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17085 if(this.allowBlank && !this.pageSize && !this.disableClear){
17086 this.footer = this.list.createChild({cls:cls+'-ft'});
17087 this.pageTb = new Roo.Toolbar(this.footer);
17091 this.footer = this.list.createChild({cls:cls+'-ft'});
17092 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17093 {pageSize: this.pageSize});
17097 if (this.pageTb && this.allowBlank && !this.disableClear) {
17099 this.pageTb.add(new Roo.Toolbar.Fill(), {
17100 cls: 'x-btn-icon x-btn-clear',
17102 handler: function()
17105 _this.clearValue();
17106 _this.onSelect(false, -1);
17111 this.assetHeight += this.footer.getHeight();
17116 this.tpl = Roo.bootstrap.version == 4 ?
17117 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17118 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17121 this.view = new Roo.View(this.list, this.tpl, {
17122 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17124 //this.view.wrapEl.setDisplayed(false);
17125 this.view.on('click', this.onViewClick, this);
17128 this.store.on('beforeload', this.onBeforeLoad, this);
17129 this.store.on('load', this.onLoad, this);
17130 this.store.on('loadexception', this.onLoadException, this);
17132 if(this.resizable){
17133 this.resizer = new Roo.Resizable(this.list, {
17134 pinned:true, handles:'se'
17136 this.resizer.on('resize', function(r, w, h){
17137 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17138 this.listWidth = w;
17139 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17140 this.restrictHeight();
17142 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17145 if(!this.editable){
17146 this.editable = true;
17147 this.setEditable(false);
17152 if (typeof(this.events.add.listeners) != 'undefined') {
17154 this.addicon = this.wrap.createChild(
17155 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17157 this.addicon.on('click', function(e) {
17158 this.fireEvent('add', this);
17161 if (typeof(this.events.edit.listeners) != 'undefined') {
17163 this.editicon = this.wrap.createChild(
17164 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17165 if (this.addicon) {
17166 this.editicon.setStyle('margin-left', '40px');
17168 this.editicon.on('click', function(e) {
17170 // we fire even if inothing is selected..
17171 this.fireEvent('edit', this, this.lastData );
17177 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17178 "up" : function(e){
17179 this.inKeyMode = true;
17183 "down" : function(e){
17184 if(!this.isExpanded()){
17185 this.onTriggerClick();
17187 this.inKeyMode = true;
17192 "enter" : function(e){
17193 // this.onViewClick();
17197 if(this.fireEvent("specialkey", this, e)){
17198 this.onViewClick(false);
17204 "esc" : function(e){
17208 "tab" : function(e){
17211 if(this.fireEvent("specialkey", this, e)){
17212 this.onViewClick(false);
17220 doRelay : function(foo, bar, hname){
17221 if(hname == 'down' || this.scope.isExpanded()){
17222 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17231 this.queryDelay = Math.max(this.queryDelay || 10,
17232 this.mode == 'local' ? 10 : 250);
17235 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17237 if(this.typeAhead){
17238 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17240 if(this.editable !== false){
17241 this.inputEl().on("keyup", this.onKeyUp, this);
17243 if(this.forceSelection){
17244 this.inputEl().on('blur', this.doForce, this);
17248 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17249 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17253 initTickableEvents: function()
17257 if(this.hiddenName){
17259 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17261 this.hiddenField.dom.value =
17262 this.hiddenValue !== undefined ? this.hiddenValue :
17263 this.value !== undefined ? this.value : '';
17265 // prevent input submission
17266 this.el.dom.removeAttribute('name');
17267 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17272 // this.list = this.el.select('ul.dropdown-menu',true).first();
17274 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17275 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17276 if(this.triggerList){
17277 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17280 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17281 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17283 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17284 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17286 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17287 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17289 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17290 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17291 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17294 this.cancelBtn.hide();
17299 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17300 _this.list.setWidth(lw);
17303 this.list.on('mouseover', this.onViewOver, this);
17304 this.list.on('mousemove', this.onViewMove, this);
17306 this.list.on('scroll', this.onViewScroll, this);
17309 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17310 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17313 this.view = new Roo.View(this.list, this.tpl, {
17318 selectedClass: this.selectedClass
17321 //this.view.wrapEl.setDisplayed(false);
17322 this.view.on('click', this.onViewClick, this);
17326 this.store.on('beforeload', this.onBeforeLoad, this);
17327 this.store.on('load', this.onLoad, this);
17328 this.store.on('loadexception', this.onLoadException, this);
17331 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17332 "up" : function(e){
17333 this.inKeyMode = true;
17337 "down" : function(e){
17338 this.inKeyMode = true;
17342 "enter" : function(e){
17343 if(this.fireEvent("specialkey", this, e)){
17344 this.onViewClick(false);
17350 "esc" : function(e){
17351 this.onTickableFooterButtonClick(e, false, false);
17354 "tab" : function(e){
17355 this.fireEvent("specialkey", this, e);
17357 this.onTickableFooterButtonClick(e, false, false);
17364 doRelay : function(e, fn, key){
17365 if(this.scope.isExpanded()){
17366 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17375 this.queryDelay = Math.max(this.queryDelay || 10,
17376 this.mode == 'local' ? 10 : 250);
17379 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17381 if(this.typeAhead){
17382 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17385 if(this.editable !== false){
17386 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17389 this.indicator = this.indicatorEl();
17391 if(this.indicator){
17392 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17393 this.indicator.hide();
17398 onDestroy : function(){
17400 this.view.setStore(null);
17401 this.view.el.removeAllListeners();
17402 this.view.el.remove();
17403 this.view.purgeListeners();
17406 this.list.dom.innerHTML = '';
17410 this.store.un('beforeload', this.onBeforeLoad, this);
17411 this.store.un('load', this.onLoad, this);
17412 this.store.un('loadexception', this.onLoadException, this);
17414 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17418 fireKey : function(e){
17419 if(e.isNavKeyPress() && !this.list.isVisible()){
17420 this.fireEvent("specialkey", this, e);
17425 onResize: function(w, h)
17429 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17431 // if(typeof w != 'number'){
17432 // // we do not handle it!?!?
17435 // var tw = this.trigger.getWidth();
17436 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17437 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17439 // this.inputEl().setWidth( this.adjustWidth('input', x));
17441 // //this.trigger.setStyle('left', x+'px');
17443 // if(this.list && this.listWidth === undefined){
17444 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17445 // this.list.setWidth(lw);
17446 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17454 * Allow or prevent the user from directly editing the field text. If false is passed,
17455 * the user will only be able to select from the items defined in the dropdown list. This method
17456 * is the runtime equivalent of setting the 'editable' config option at config time.
17457 * @param {Boolean} value True to allow the user to directly edit the field text
17459 setEditable : function(value){
17460 if(value == this.editable){
17463 this.editable = value;
17465 this.inputEl().dom.setAttribute('readOnly', true);
17466 this.inputEl().on('mousedown', this.onTriggerClick, this);
17467 this.inputEl().addClass('x-combo-noedit');
17469 this.inputEl().dom.removeAttribute('readOnly');
17470 this.inputEl().un('mousedown', this.onTriggerClick, this);
17471 this.inputEl().removeClass('x-combo-noedit');
17477 onBeforeLoad : function(combo,opts){
17478 if(!this.hasFocus){
17482 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17484 this.restrictHeight();
17485 this.selectedIndex = -1;
17489 onLoad : function(){
17491 this.hasQuery = false;
17493 if(!this.hasFocus){
17497 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17498 this.loading.hide();
17501 if(this.store.getCount() > 0){
17504 this.restrictHeight();
17505 if(this.lastQuery == this.allQuery){
17506 if(this.editable && !this.tickable){
17507 this.inputEl().dom.select();
17511 !this.selectByValue(this.value, true) &&
17514 !this.store.lastOptions ||
17515 typeof(this.store.lastOptions.add) == 'undefined' ||
17516 this.store.lastOptions.add != true
17519 this.select(0, true);
17522 if(this.autoFocus){
17525 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17526 this.taTask.delay(this.typeAheadDelay);
17530 this.onEmptyResults();
17536 onLoadException : function()
17538 this.hasQuery = false;
17540 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17541 this.loading.hide();
17544 if(this.tickable && this.editable){
17549 // only causes errors at present
17550 //Roo.log(this.store.reader.jsonData);
17551 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17553 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17559 onTypeAhead : function(){
17560 if(this.store.getCount() > 0){
17561 var r = this.store.getAt(0);
17562 var newValue = r.data[this.displayField];
17563 var len = newValue.length;
17564 var selStart = this.getRawValue().length;
17566 if(selStart != len){
17567 this.setRawValue(newValue);
17568 this.selectText(selStart, newValue.length);
17574 onSelect : function(record, index){
17576 if(this.fireEvent('beforeselect', this, record, index) !== false){
17578 this.setFromData(index > -1 ? record.data : false);
17581 this.fireEvent('select', this, record, index);
17586 * Returns the currently selected field value or empty string if no value is set.
17587 * @return {String} value The selected value
17589 getValue : function()
17591 if(Roo.isIOS && this.useNativeIOS){
17592 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17596 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17599 if(this.valueField){
17600 return typeof this.value != 'undefined' ? this.value : '';
17602 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17606 getRawValue : function()
17608 if(Roo.isIOS && this.useNativeIOS){
17609 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17612 var v = this.inputEl().getValue();
17618 * Clears any text/value currently set in the field
17620 clearValue : function(){
17622 if(this.hiddenField){
17623 this.hiddenField.dom.value = '';
17626 this.setRawValue('');
17627 this.lastSelectionText = '';
17628 this.lastData = false;
17630 var close = this.closeTriggerEl();
17641 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17642 * will be displayed in the field. If the value does not match the data value of an existing item,
17643 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17644 * Otherwise the field will be blank (although the value will still be set).
17645 * @param {String} value The value to match
17647 setValue : function(v)
17649 if(Roo.isIOS && this.useNativeIOS){
17650 this.setIOSValue(v);
17660 if(this.valueField){
17661 var r = this.findRecord(this.valueField, v);
17663 text = r.data[this.displayField];
17664 }else if(this.valueNotFoundText !== undefined){
17665 text = this.valueNotFoundText;
17668 this.lastSelectionText = text;
17669 if(this.hiddenField){
17670 this.hiddenField.dom.value = v;
17672 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17675 var close = this.closeTriggerEl();
17678 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17684 * @property {Object} the last set data for the element
17689 * Sets the value of the field based on a object which is related to the record format for the store.
17690 * @param {Object} value the value to set as. or false on reset?
17692 setFromData : function(o){
17699 var dv = ''; // display value
17700 var vv = ''; // value value..
17702 if (this.displayField) {
17703 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17705 // this is an error condition!!!
17706 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17709 if(this.valueField){
17710 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17713 var close = this.closeTriggerEl();
17716 if(dv.length || vv * 1 > 0){
17718 this.blockFocus=true;
17724 if(this.hiddenField){
17725 this.hiddenField.dom.value = vv;
17727 this.lastSelectionText = dv;
17728 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17732 // no hidden field.. - we store the value in 'value', but still display
17733 // display field!!!!
17734 this.lastSelectionText = dv;
17735 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17742 reset : function(){
17743 // overridden so that last data is reset..
17750 this.setValue(this.originalValue);
17751 //this.clearInvalid();
17752 this.lastData = false;
17754 this.view.clearSelections();
17760 findRecord : function(prop, value){
17762 if(this.store.getCount() > 0){
17763 this.store.each(function(r){
17764 if(r.data[prop] == value){
17774 getName: function()
17776 // returns hidden if it's set..
17777 if (!this.rendered) {return ''};
17778 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17782 onViewMove : function(e, t){
17783 this.inKeyMode = false;
17787 onViewOver : function(e, t){
17788 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17791 var item = this.view.findItemFromChild(t);
17794 var index = this.view.indexOf(item);
17795 this.select(index, false);
17800 onViewClick : function(view, doFocus, el, e)
17802 var index = this.view.getSelectedIndexes()[0];
17804 var r = this.store.getAt(index);
17808 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17815 Roo.each(this.tickItems, function(v,k){
17817 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17819 _this.tickItems.splice(k, 1);
17821 if(typeof(e) == 'undefined' && view == false){
17822 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17834 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17835 this.tickItems.push(r.data);
17838 if(typeof(e) == 'undefined' && view == false){
17839 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17846 this.onSelect(r, index);
17848 if(doFocus !== false && !this.blockFocus){
17849 this.inputEl().focus();
17854 restrictHeight : function(){
17855 //this.innerList.dom.style.height = '';
17856 //var inner = this.innerList.dom;
17857 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17858 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17859 //this.list.beginUpdate();
17860 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17861 this.list.alignTo(this.inputEl(), this.listAlign);
17862 this.list.alignTo(this.inputEl(), this.listAlign);
17863 //this.list.endUpdate();
17867 onEmptyResults : function(){
17869 if(this.tickable && this.editable){
17870 this.hasFocus = false;
17871 this.restrictHeight();
17879 * Returns true if the dropdown list is expanded, else false.
17881 isExpanded : function(){
17882 return this.list.isVisible();
17886 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17887 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17888 * @param {String} value The data value of the item to select
17889 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17890 * selected item if it is not currently in view (defaults to true)
17891 * @return {Boolean} True if the value matched an item in the list, else false
17893 selectByValue : function(v, scrollIntoView){
17894 if(v !== undefined && v !== null){
17895 var r = this.findRecord(this.valueField || this.displayField, v);
17897 this.select(this.store.indexOf(r), scrollIntoView);
17905 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17906 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17907 * @param {Number} index The zero-based index of the list item to select
17908 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17909 * selected item if it is not currently in view (defaults to true)
17911 select : function(index, scrollIntoView){
17912 this.selectedIndex = index;
17913 this.view.select(index);
17914 if(scrollIntoView !== false){
17915 var el = this.view.getNode(index);
17917 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17920 this.list.scrollChildIntoView(el, false);
17926 selectNext : function(){
17927 var ct = this.store.getCount();
17929 if(this.selectedIndex == -1){
17931 }else if(this.selectedIndex < ct-1){
17932 this.select(this.selectedIndex+1);
17938 selectPrev : function(){
17939 var ct = this.store.getCount();
17941 if(this.selectedIndex == -1){
17943 }else if(this.selectedIndex != 0){
17944 this.select(this.selectedIndex-1);
17950 onKeyUp : function(e){
17951 if(this.editable !== false && !e.isSpecialKey()){
17952 this.lastKey = e.getKey();
17953 this.dqTask.delay(this.queryDelay);
17958 validateBlur : function(){
17959 return !this.list || !this.list.isVisible();
17963 initQuery : function(){
17965 var v = this.getRawValue();
17967 if(this.tickable && this.editable){
17968 v = this.tickableInputEl().getValue();
17975 doForce : function(){
17976 if(this.inputEl().dom.value.length > 0){
17977 this.inputEl().dom.value =
17978 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17984 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17985 * query allowing the query action to be canceled if needed.
17986 * @param {String} query The SQL query to execute
17987 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17988 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17989 * saved in the current store (defaults to false)
17991 doQuery : function(q, forceAll){
17993 if(q === undefined || q === null){
17998 forceAll: forceAll,
18002 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18007 forceAll = qe.forceAll;
18008 if(forceAll === true || (q.length >= this.minChars)){
18010 this.hasQuery = true;
18012 if(this.lastQuery != q || this.alwaysQuery){
18013 this.lastQuery = q;
18014 if(this.mode == 'local'){
18015 this.selectedIndex = -1;
18017 this.store.clearFilter();
18020 if(this.specialFilter){
18021 this.fireEvent('specialfilter', this);
18026 this.store.filter(this.displayField, q);
18029 this.store.fireEvent("datachanged", this.store);
18036 this.store.baseParams[this.queryParam] = q;
18038 var options = {params : this.getParams(q)};
18041 options.add = true;
18042 options.params.start = this.page * this.pageSize;
18045 this.store.load(options);
18048 * this code will make the page width larger, at the beginning, the list not align correctly,
18049 * we should expand the list on onLoad
18050 * so command out it
18055 this.selectedIndex = -1;
18060 this.loadNext = false;
18064 getParams : function(q){
18066 //p[this.queryParam] = q;
18070 p.limit = this.pageSize;
18076 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18078 collapse : function(){
18079 if(!this.isExpanded()){
18085 this.hasFocus = false;
18089 this.cancelBtn.hide();
18090 this.trigger.show();
18093 this.tickableInputEl().dom.value = '';
18094 this.tickableInputEl().blur();
18099 Roo.get(document).un('mousedown', this.collapseIf, this);
18100 Roo.get(document).un('mousewheel', this.collapseIf, this);
18101 if (!this.editable) {
18102 Roo.get(document).un('keydown', this.listKeyPress, this);
18104 this.fireEvent('collapse', this);
18110 collapseIf : function(e){
18111 var in_combo = e.within(this.el);
18112 var in_list = e.within(this.list);
18113 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18115 if (in_combo || in_list || is_list) {
18116 //e.stopPropagation();
18121 this.onTickableFooterButtonClick(e, false, false);
18129 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18131 expand : function(){
18133 if(this.isExpanded() || !this.hasFocus){
18137 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18138 this.list.setWidth(lw);
18144 this.restrictHeight();
18148 this.tickItems = Roo.apply([], this.item);
18151 this.cancelBtn.show();
18152 this.trigger.hide();
18155 this.tickableInputEl().focus();
18160 Roo.get(document).on('mousedown', this.collapseIf, this);
18161 Roo.get(document).on('mousewheel', this.collapseIf, this);
18162 if (!this.editable) {
18163 Roo.get(document).on('keydown', this.listKeyPress, this);
18166 this.fireEvent('expand', this);
18170 // Implements the default empty TriggerField.onTriggerClick function
18171 onTriggerClick : function(e)
18173 Roo.log('trigger click');
18175 if(this.disabled || !this.triggerList){
18180 this.loadNext = false;
18182 if(this.isExpanded()){
18184 if (!this.blockFocus) {
18185 this.inputEl().focus();
18189 this.hasFocus = true;
18190 if(this.triggerAction == 'all') {
18191 this.doQuery(this.allQuery, true);
18193 this.doQuery(this.getRawValue());
18195 if (!this.blockFocus) {
18196 this.inputEl().focus();
18201 onTickableTriggerClick : function(e)
18208 this.loadNext = false;
18209 this.hasFocus = true;
18211 if(this.triggerAction == 'all') {
18212 this.doQuery(this.allQuery, true);
18214 this.doQuery(this.getRawValue());
18218 onSearchFieldClick : function(e)
18220 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18221 this.onTickableFooterButtonClick(e, false, false);
18225 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18230 this.loadNext = false;
18231 this.hasFocus = true;
18233 if(this.triggerAction == 'all') {
18234 this.doQuery(this.allQuery, true);
18236 this.doQuery(this.getRawValue());
18240 listKeyPress : function(e)
18242 //Roo.log('listkeypress');
18243 // scroll to first matching element based on key pres..
18244 if (e.isSpecialKey()) {
18247 var k = String.fromCharCode(e.getKey()).toUpperCase();
18250 var csel = this.view.getSelectedNodes();
18251 var cselitem = false;
18253 var ix = this.view.indexOf(csel[0]);
18254 cselitem = this.store.getAt(ix);
18255 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18261 this.store.each(function(v) {
18263 // start at existing selection.
18264 if (cselitem.id == v.id) {
18270 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18271 match = this.store.indexOf(v);
18277 if (match === false) {
18278 return true; // no more action?
18281 this.view.select(match);
18282 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18283 sn.scrollIntoView(sn.dom.parentNode, false);
18286 onViewScroll : function(e, t){
18288 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){
18292 this.hasQuery = true;
18294 this.loading = this.list.select('.loading', true).first();
18296 if(this.loading === null){
18297 this.list.createChild({
18299 cls: 'loading roo-select2-more-results roo-select2-active',
18300 html: 'Loading more results...'
18303 this.loading = this.list.select('.loading', true).first();
18305 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18307 this.loading.hide();
18310 this.loading.show();
18315 this.loadNext = true;
18317 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18322 addItem : function(o)
18324 var dv = ''; // display value
18326 if (this.displayField) {
18327 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18329 // this is an error condition!!!
18330 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18337 var choice = this.choices.createChild({
18339 cls: 'roo-select2-search-choice',
18348 cls: 'roo-select2-search-choice-close fa fa-times',
18353 }, this.searchField);
18355 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18357 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18365 this.inputEl().dom.value = '';
18370 onRemoveItem : function(e, _self, o)
18372 e.preventDefault();
18374 this.lastItem = Roo.apply([], this.item);
18376 var index = this.item.indexOf(o.data) * 1;
18379 Roo.log('not this item?!');
18383 this.item.splice(index, 1);
18388 this.fireEvent('remove', this, e);
18394 syncValue : function()
18396 if(!this.item.length){
18403 Roo.each(this.item, function(i){
18404 if(_this.valueField){
18405 value.push(i[_this.valueField]);
18412 this.value = value.join(',');
18414 if(this.hiddenField){
18415 this.hiddenField.dom.value = this.value;
18418 this.store.fireEvent("datachanged", this.store);
18423 clearItem : function()
18425 if(!this.multiple){
18431 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18439 if(this.tickable && !Roo.isTouch){
18440 this.view.refresh();
18444 inputEl: function ()
18446 if(Roo.isIOS && this.useNativeIOS){
18447 return this.el.select('select.roo-ios-select', true).first();
18450 if(Roo.isTouch && this.mobileTouchView){
18451 return this.el.select('input.form-control',true).first();
18455 return this.searchField;
18458 return this.el.select('input.form-control',true).first();
18461 onTickableFooterButtonClick : function(e, btn, el)
18463 e.preventDefault();
18465 this.lastItem = Roo.apply([], this.item);
18467 if(btn && btn.name == 'cancel'){
18468 this.tickItems = Roo.apply([], this.item);
18477 Roo.each(this.tickItems, function(o){
18485 validate : function()
18487 if(this.getVisibilityEl().hasClass('hidden')){
18491 var v = this.getRawValue();
18494 v = this.getValue();
18497 if(this.disabled || this.allowBlank || v.length){
18502 this.markInvalid();
18506 tickableInputEl : function()
18508 if(!this.tickable || !this.editable){
18509 return this.inputEl();
18512 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18516 getAutoCreateTouchView : function()
18521 cls: 'form-group' //input-group
18527 type : this.inputType,
18528 cls : 'form-control x-combo-noedit',
18529 autocomplete: 'new-password',
18530 placeholder : this.placeholder || '',
18535 input.name = this.name;
18539 input.cls += ' input-' + this.size;
18542 if (this.disabled) {
18543 input.disabled = true;
18547 cls : 'roo-combobox-wrap',
18554 inputblock.cls += ' input-group';
18556 inputblock.cn.unshift({
18558 cls : 'input-group-addon input-group-prepend input-group-text',
18563 if(this.removable && !this.multiple){
18564 inputblock.cls += ' roo-removable';
18566 inputblock.cn.push({
18569 cls : 'roo-combo-removable-btn close'
18573 if(this.hasFeedback && !this.allowBlank){
18575 inputblock.cls += ' has-feedback';
18577 inputblock.cn.push({
18579 cls: 'glyphicon form-control-feedback'
18586 inputblock.cls += (this.before) ? '' : ' input-group';
18588 inputblock.cn.push({
18590 cls : 'input-group-addon input-group-append input-group-text',
18596 var ibwrap = inputblock;
18601 cls: 'roo-select2-choices',
18605 cls: 'roo-select2-search-field',
18618 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18623 cls: 'form-hidden-field'
18629 if(!this.multiple && this.showToggleBtn){
18635 if (this.caret != false) {
18638 cls: 'fa fa-' + this.caret
18645 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18647 Roo.bootstrap.version == 3 ? caret : '',
18650 cls: 'combobox-clear',
18664 combobox.cls += ' roo-select2-container-multi';
18667 var required = this.allowBlank ? {
18669 style: 'display: none'
18672 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18673 tooltip : 'This field is required'
18676 var align = this.labelAlign || this.parentLabelAlign();
18678 if (align ==='left' && this.fieldLabel.length) {
18684 cls : 'control-label col-form-label',
18685 html : this.fieldLabel
18689 cls : 'roo-combobox-wrap ',
18696 var labelCfg = cfg.cn[1];
18697 var contentCfg = cfg.cn[2];
18700 if(this.indicatorpos == 'right'){
18705 cls : 'control-label col-form-label',
18709 html : this.fieldLabel
18715 cls : "roo-combobox-wrap ",
18723 labelCfg = cfg.cn[0];
18724 contentCfg = cfg.cn[1];
18729 if(this.labelWidth > 12){
18730 labelCfg.style = "width: " + this.labelWidth + 'px';
18733 if(this.labelWidth < 13 && this.labelmd == 0){
18734 this.labelmd = this.labelWidth;
18737 if(this.labellg > 0){
18738 labelCfg.cls += ' col-lg-' + this.labellg;
18739 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18742 if(this.labelmd > 0){
18743 labelCfg.cls += ' col-md-' + this.labelmd;
18744 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18747 if(this.labelsm > 0){
18748 labelCfg.cls += ' col-sm-' + this.labelsm;
18749 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18752 if(this.labelxs > 0){
18753 labelCfg.cls += ' col-xs-' + this.labelxs;
18754 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18758 } else if ( this.fieldLabel.length) {
18763 cls : 'control-label',
18764 html : this.fieldLabel
18775 if(this.indicatorpos == 'right'){
18779 cls : 'control-label',
18780 html : this.fieldLabel,
18798 var settings = this;
18800 ['xs','sm','md','lg'].map(function(size){
18801 if (settings[size]) {
18802 cfg.cls += ' col-' + size + '-' + settings[size];
18809 initTouchView : function()
18811 this.renderTouchView();
18813 this.touchViewEl.on('scroll', function(){
18814 this.el.dom.scrollTop = 0;
18817 this.originalValue = this.getValue();
18819 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18821 this.inputEl().on("click", this.showTouchView, this);
18822 if (this.triggerEl) {
18823 this.triggerEl.on("click", this.showTouchView, this);
18827 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18828 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18830 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18832 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18833 this.store.on('load', this.onTouchViewLoad, this);
18834 this.store.on('loadexception', this.onTouchViewLoadException, this);
18836 if(this.hiddenName){
18838 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18840 this.hiddenField.dom.value =
18841 this.hiddenValue !== undefined ? this.hiddenValue :
18842 this.value !== undefined ? this.value : '';
18844 this.el.dom.removeAttribute('name');
18845 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18849 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18850 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18853 if(this.removable && !this.multiple){
18854 var close = this.closeTriggerEl();
18856 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18857 close.on('click', this.removeBtnClick, this, close);
18861 * fix the bug in Safari iOS8
18863 this.inputEl().on("focus", function(e){
18864 document.activeElement.blur();
18867 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18874 renderTouchView : function()
18876 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18877 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18879 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18880 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18882 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18883 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884 this.touchViewBodyEl.setStyle('overflow', 'auto');
18886 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18887 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18889 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18890 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18894 showTouchView : function()
18900 this.touchViewHeaderEl.hide();
18902 if(this.modalTitle.length){
18903 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18904 this.touchViewHeaderEl.show();
18907 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18908 this.touchViewEl.show();
18910 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18912 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18913 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18915 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18917 if(this.modalTitle.length){
18918 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18921 this.touchViewBodyEl.setHeight(bodyHeight);
18925 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18927 this.touchViewEl.addClass(['in','show']);
18930 if(this._touchViewMask){
18931 Roo.get(document.body).addClass("x-body-masked");
18932 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18933 this._touchViewMask.setStyle('z-index', 10000);
18934 this._touchViewMask.addClass('show');
18937 this.doTouchViewQuery();
18941 hideTouchView : function()
18943 this.touchViewEl.removeClass(['in','show']);
18947 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18949 this.touchViewEl.setStyle('display', 'none');
18952 if(this._touchViewMask){
18953 this._touchViewMask.removeClass('show');
18954 Roo.get(document.body).removeClass("x-body-masked");
18958 setTouchViewValue : function()
18965 Roo.each(this.tickItems, function(o){
18970 this.hideTouchView();
18973 doTouchViewQuery : function()
18982 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18986 if(!this.alwaysQuery || this.mode == 'local'){
18987 this.onTouchViewLoad();
18994 onTouchViewBeforeLoad : function(combo,opts)
19000 onTouchViewLoad : function()
19002 if(this.store.getCount() < 1){
19003 this.onTouchViewEmptyResults();
19007 this.clearTouchView();
19009 var rawValue = this.getRawValue();
19011 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19013 this.tickItems = [];
19015 this.store.data.each(function(d, rowIndex){
19016 var row = this.touchViewListGroup.createChild(template);
19018 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19019 row.addClass(d.data.cls);
19022 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19025 html : d.data[this.displayField]
19028 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19029 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19032 row.removeClass('selected');
19033 if(!this.multiple && this.valueField &&
19034 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19037 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19038 row.addClass('selected');
19041 if(this.multiple && this.valueField &&
19042 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19046 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19047 this.tickItems.push(d.data);
19050 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19054 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19056 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19058 if(this.modalTitle.length){
19059 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19062 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19064 if(this.mobile_restrict_height && listHeight < bodyHeight){
19065 this.touchViewBodyEl.setHeight(listHeight);
19070 if(firstChecked && listHeight > bodyHeight){
19071 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19076 onTouchViewLoadException : function()
19078 this.hideTouchView();
19081 onTouchViewEmptyResults : function()
19083 this.clearTouchView();
19085 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19087 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19091 clearTouchView : function()
19093 this.touchViewListGroup.dom.innerHTML = '';
19096 onTouchViewClick : function(e, el, o)
19098 e.preventDefault();
19101 var rowIndex = o.rowIndex;
19103 var r = this.store.getAt(rowIndex);
19105 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19107 if(!this.multiple){
19108 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19109 c.dom.removeAttribute('checked');
19112 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19114 this.setFromData(r.data);
19116 var close = this.closeTriggerEl();
19122 this.hideTouchView();
19124 this.fireEvent('select', this, r, rowIndex);
19129 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19130 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19131 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19135 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19136 this.addItem(r.data);
19137 this.tickItems.push(r.data);
19141 getAutoCreateNativeIOS : function()
19144 cls: 'form-group' //input-group,
19149 cls : 'roo-ios-select'
19153 combobox.name = this.name;
19156 if (this.disabled) {
19157 combobox.disabled = true;
19160 var settings = this;
19162 ['xs','sm','md','lg'].map(function(size){
19163 if (settings[size]) {
19164 cfg.cls += ' col-' + size + '-' + settings[size];
19174 initIOSView : function()
19176 this.store.on('load', this.onIOSViewLoad, this);
19181 onIOSViewLoad : function()
19183 if(this.store.getCount() < 1){
19187 this.clearIOSView();
19189 if(this.allowBlank) {
19191 var default_text = '-- SELECT --';
19193 if(this.placeholder.length){
19194 default_text = this.placeholder;
19197 if(this.emptyTitle.length){
19198 default_text += ' - ' + this.emptyTitle + ' -';
19201 var opt = this.inputEl().createChild({
19204 html : default_text
19208 o[this.valueField] = 0;
19209 o[this.displayField] = default_text;
19211 this.ios_options.push({
19218 this.store.data.each(function(d, rowIndex){
19222 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19223 html = d.data[this.displayField];
19228 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19229 value = d.data[this.valueField];
19238 if(this.value == d.data[this.valueField]){
19239 option['selected'] = true;
19242 var opt = this.inputEl().createChild(option);
19244 this.ios_options.push({
19251 this.inputEl().on('change', function(){
19252 this.fireEvent('select', this);
19257 clearIOSView: function()
19259 this.inputEl().dom.innerHTML = '';
19261 this.ios_options = [];
19264 setIOSValue: function(v)
19268 if(!this.ios_options){
19272 Roo.each(this.ios_options, function(opts){
19274 opts.el.dom.removeAttribute('selected');
19276 if(opts.data[this.valueField] != v){
19280 opts.el.dom.setAttribute('selected', true);
19286 * @cfg {Boolean} grow
19290 * @cfg {Number} growMin
19294 * @cfg {Number} growMax
19303 Roo.apply(Roo.bootstrap.ComboBox, {
19307 cls: 'modal-header',
19329 cls: 'list-group-item',
19333 cls: 'roo-combobox-list-group-item-value'
19337 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19351 listItemCheckbox : {
19353 cls: 'list-group-item',
19357 cls: 'roo-combobox-list-group-item-value'
19361 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19377 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19382 cls: 'modal-footer',
19390 cls: 'col-xs-6 text-left',
19393 cls: 'btn btn-danger roo-touch-view-cancel',
19399 cls: 'col-xs-6 text-right',
19402 cls: 'btn btn-success roo-touch-view-ok',
19413 Roo.apply(Roo.bootstrap.ComboBox, {
19415 touchViewTemplate : {
19417 cls: 'modal fade roo-combobox-touch-view',
19421 cls: 'modal-dialog',
19422 style : 'position:fixed', // we have to fix position....
19426 cls: 'modal-content',
19428 Roo.bootstrap.ComboBox.header,
19429 Roo.bootstrap.ComboBox.body,
19430 Roo.bootstrap.ComboBox.footer
19439 * Ext JS Library 1.1.1
19440 * Copyright(c) 2006-2007, Ext JS, LLC.
19442 * Originally Released Under LGPL - original licence link has changed is not relivant.
19445 * <script type="text/javascript">
19450 * @extends Roo.util.Observable
19451 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19452 * This class also supports single and multi selection modes. <br>
19453 * Create a data model bound view:
19455 var store = new Roo.data.Store(...);
19457 var view = new Roo.View({
19459 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19461 singleSelect: true,
19462 selectedClass: "ydataview-selected",
19466 // listen for node click?
19467 view.on("click", function(vw, index, node, e){
19468 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19472 dataModel.load("foobar.xml");
19474 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19476 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19477 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19479 * Note: old style constructor is still suported (container, template, config)
19482 * Create a new View
19483 * @param {Object} config The config object
19486 Roo.View = function(config, depreciated_tpl, depreciated_config){
19488 this.parent = false;
19490 if (typeof(depreciated_tpl) == 'undefined') {
19491 // new way.. - universal constructor.
19492 Roo.apply(this, config);
19493 this.el = Roo.get(this.el);
19496 this.el = Roo.get(config);
19497 this.tpl = depreciated_tpl;
19498 Roo.apply(this, depreciated_config);
19500 this.wrapEl = this.el.wrap().wrap();
19501 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19504 if(typeof(this.tpl) == "string"){
19505 this.tpl = new Roo.Template(this.tpl);
19507 // support xtype ctors..
19508 this.tpl = new Roo.factory(this.tpl, Roo);
19512 this.tpl.compile();
19517 * @event beforeclick
19518 * Fires before a click is processed. Returns false to cancel the default action.
19519 * @param {Roo.View} this
19520 * @param {Number} index The index of the target node
19521 * @param {HTMLElement} node The target node
19522 * @param {Roo.EventObject} e The raw event object
19524 "beforeclick" : true,
19527 * Fires when a template node is clicked.
19528 * @param {Roo.View} this
19529 * @param {Number} index The index of the target node
19530 * @param {HTMLElement} node The target node
19531 * @param {Roo.EventObject} e The raw event object
19536 * Fires when a template node is double clicked.
19537 * @param {Roo.View} this
19538 * @param {Number} index The index of the target node
19539 * @param {HTMLElement} node The target node
19540 * @param {Roo.EventObject} e The raw event object
19544 * @event contextmenu
19545 * Fires when a template node is right clicked.
19546 * @param {Roo.View} this
19547 * @param {Number} index The index of the target node
19548 * @param {HTMLElement} node The target node
19549 * @param {Roo.EventObject} e The raw event object
19551 "contextmenu" : true,
19553 * @event selectionchange
19554 * Fires when the selected nodes change.
19555 * @param {Roo.View} this
19556 * @param {Array} selections Array of the selected nodes
19558 "selectionchange" : true,
19561 * @event beforeselect
19562 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19563 * @param {Roo.View} this
19564 * @param {HTMLElement} node The node to be selected
19565 * @param {Array} selections Array of currently selected nodes
19567 "beforeselect" : true,
19569 * @event preparedata
19570 * Fires on every row to render, to allow you to change the data.
19571 * @param {Roo.View} this
19572 * @param {Object} data to be rendered (change this)
19574 "preparedata" : true
19582 "click": this.onClick,
19583 "dblclick": this.onDblClick,
19584 "contextmenu": this.onContextMenu,
19588 this.selections = [];
19590 this.cmp = new Roo.CompositeElementLite([]);
19592 this.store = Roo.factory(this.store, Roo.data);
19593 this.setStore(this.store, true);
19596 if ( this.footer && this.footer.xtype) {
19598 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19600 this.footer.dataSource = this.store;
19601 this.footer.container = fctr;
19602 this.footer = Roo.factory(this.footer, Roo);
19603 fctr.insertFirst(this.el);
19605 // this is a bit insane - as the paging toolbar seems to detach the el..
19606 // dom.parentNode.parentNode.parentNode
19607 // they get detached?
19611 Roo.View.superclass.constructor.call(this);
19616 Roo.extend(Roo.View, Roo.util.Observable, {
19619 * @cfg {Roo.data.Store} store Data store to load data from.
19624 * @cfg {String|Roo.Element} el The container element.
19629 * @cfg {String|Roo.Template} tpl The template used by this View
19633 * @cfg {String} dataName the named area of the template to use as the data area
19634 * Works with domtemplates roo-name="name"
19638 * @cfg {String} selectedClass The css class to add to selected nodes
19640 selectedClass : "x-view-selected",
19642 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19647 * @cfg {String} text to display on mask (default Loading)
19651 * @cfg {Boolean} multiSelect Allow multiple selection
19653 multiSelect : false,
19655 * @cfg {Boolean} singleSelect Allow single selection
19657 singleSelect: false,
19660 * @cfg {Boolean} toggleSelect - selecting
19662 toggleSelect : false,
19665 * @cfg {Boolean} tickable - selecting
19670 * Returns the element this view is bound to.
19671 * @return {Roo.Element}
19673 getEl : function(){
19674 return this.wrapEl;
19680 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19682 refresh : function(){
19683 //Roo.log('refresh');
19686 // if we are using something like 'domtemplate', then
19687 // the what gets used is:
19688 // t.applySubtemplate(NAME, data, wrapping data..)
19689 // the outer template then get' applied with
19690 // the store 'extra data'
19691 // and the body get's added to the
19692 // roo-name="data" node?
19693 // <span class='roo-tpl-{name}'></span> ?????
19697 this.clearSelections();
19698 this.el.update("");
19700 var records = this.store.getRange();
19701 if(records.length < 1) {
19703 // is this valid?? = should it render a template??
19705 this.el.update(this.emptyText);
19709 if (this.dataName) {
19710 this.el.update(t.apply(this.store.meta)); //????
19711 el = this.el.child('.roo-tpl-' + this.dataName);
19714 for(var i = 0, len = records.length; i < len; i++){
19715 var data = this.prepareData(records[i].data, i, records[i]);
19716 this.fireEvent("preparedata", this, data, i, records[i]);
19718 var d = Roo.apply({}, data);
19721 Roo.apply(d, {'roo-id' : Roo.id()});
19725 Roo.each(this.parent.item, function(item){
19726 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19729 Roo.apply(d, {'roo-data-checked' : 'checked'});
19733 html[html.length] = Roo.util.Format.trim(
19735 t.applySubtemplate(this.dataName, d, this.store.meta) :
19742 el.update(html.join(""));
19743 this.nodes = el.dom.childNodes;
19744 this.updateIndexes(0);
19749 * Function to override to reformat the data that is sent to
19750 * the template for each node.
19751 * DEPRICATED - use the preparedata event handler.
19752 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19753 * a JSON object for an UpdateManager bound view).
19755 prepareData : function(data, index, record)
19757 this.fireEvent("preparedata", this, data, index, record);
19761 onUpdate : function(ds, record){
19762 // Roo.log('on update');
19763 this.clearSelections();
19764 var index = this.store.indexOf(record);
19765 var n = this.nodes[index];
19766 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19767 n.parentNode.removeChild(n);
19768 this.updateIndexes(index, index);
19774 onAdd : function(ds, records, index)
19776 //Roo.log(['on Add', ds, records, index] );
19777 this.clearSelections();
19778 if(this.nodes.length == 0){
19782 var n = this.nodes[index];
19783 for(var i = 0, len = records.length; i < len; i++){
19784 var d = this.prepareData(records[i].data, i, records[i]);
19786 this.tpl.insertBefore(n, d);
19789 this.tpl.append(this.el, d);
19792 this.updateIndexes(index);
19795 onRemove : function(ds, record, index){
19796 // Roo.log('onRemove');
19797 this.clearSelections();
19798 var el = this.dataName ?
19799 this.el.child('.roo-tpl-' + this.dataName) :
19802 el.dom.removeChild(this.nodes[index]);
19803 this.updateIndexes(index);
19807 * Refresh an individual node.
19808 * @param {Number} index
19810 refreshNode : function(index){
19811 this.onUpdate(this.store, this.store.getAt(index));
19814 updateIndexes : function(startIndex, endIndex){
19815 var ns = this.nodes;
19816 startIndex = startIndex || 0;
19817 endIndex = endIndex || ns.length - 1;
19818 for(var i = startIndex; i <= endIndex; i++){
19819 ns[i].nodeIndex = i;
19824 * Changes the data store this view uses and refresh the view.
19825 * @param {Store} store
19827 setStore : function(store, initial){
19828 if(!initial && this.store){
19829 this.store.un("datachanged", this.refresh);
19830 this.store.un("add", this.onAdd);
19831 this.store.un("remove", this.onRemove);
19832 this.store.un("update", this.onUpdate);
19833 this.store.un("clear", this.refresh);
19834 this.store.un("beforeload", this.onBeforeLoad);
19835 this.store.un("load", this.onLoad);
19836 this.store.un("loadexception", this.onLoad);
19840 store.on("datachanged", this.refresh, this);
19841 store.on("add", this.onAdd, this);
19842 store.on("remove", this.onRemove, this);
19843 store.on("update", this.onUpdate, this);
19844 store.on("clear", this.refresh, this);
19845 store.on("beforeload", this.onBeforeLoad, this);
19846 store.on("load", this.onLoad, this);
19847 store.on("loadexception", this.onLoad, this);
19855 * onbeforeLoad - masks the loading area.
19858 onBeforeLoad : function(store,opts)
19860 //Roo.log('onBeforeLoad');
19862 this.el.update("");
19864 this.el.mask(this.mask ? this.mask : "Loading" );
19866 onLoad : function ()
19873 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19874 * @param {HTMLElement} node
19875 * @return {HTMLElement} The template node
19877 findItemFromChild : function(node){
19878 var el = this.dataName ?
19879 this.el.child('.roo-tpl-' + this.dataName,true) :
19882 if(!node || node.parentNode == el){
19885 var p = node.parentNode;
19886 while(p && p != el){
19887 if(p.parentNode == el){
19896 onClick : function(e){
19897 var item = this.findItemFromChild(e.getTarget());
19899 var index = this.indexOf(item);
19900 if(this.onItemClick(item, index, e) !== false){
19901 this.fireEvent("click", this, index, item, e);
19904 this.clearSelections();
19909 onContextMenu : function(e){
19910 var item = this.findItemFromChild(e.getTarget());
19912 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19917 onDblClick : function(e){
19918 var item = this.findItemFromChild(e.getTarget());
19920 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19924 onItemClick : function(item, index, e)
19926 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19929 if (this.toggleSelect) {
19930 var m = this.isSelected(item) ? 'unselect' : 'select';
19933 _t[m](item, true, false);
19936 if(this.multiSelect || this.singleSelect){
19937 if(this.multiSelect && e.shiftKey && this.lastSelection){
19938 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19940 this.select(item, this.multiSelect && e.ctrlKey);
19941 this.lastSelection = item;
19944 if(!this.tickable){
19945 e.preventDefault();
19953 * Get the number of selected nodes.
19956 getSelectionCount : function(){
19957 return this.selections.length;
19961 * Get the currently selected nodes.
19962 * @return {Array} An array of HTMLElements
19964 getSelectedNodes : function(){
19965 return this.selections;
19969 * Get the indexes of the selected nodes.
19972 getSelectedIndexes : function(){
19973 var indexes = [], s = this.selections;
19974 for(var i = 0, len = s.length; i < len; i++){
19975 indexes.push(s[i].nodeIndex);
19981 * Clear all selections
19982 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19984 clearSelections : function(suppressEvent){
19985 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19986 this.cmp.elements = this.selections;
19987 this.cmp.removeClass(this.selectedClass);
19988 this.selections = [];
19989 if(!suppressEvent){
19990 this.fireEvent("selectionchange", this, this.selections);
19996 * Returns true if the passed node is selected
19997 * @param {HTMLElement/Number} node The node or node index
19998 * @return {Boolean}
20000 isSelected : function(node){
20001 var s = this.selections;
20005 node = this.getNode(node);
20006 return s.indexOf(node) !== -1;
20011 * @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
20012 * @param {Boolean} keepExisting (optional) true to keep existing selections
20013 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20015 select : function(nodeInfo, keepExisting, suppressEvent){
20016 if(nodeInfo instanceof Array){
20018 this.clearSelections(true);
20020 for(var i = 0, len = nodeInfo.length; i < len; i++){
20021 this.select(nodeInfo[i], true, true);
20025 var node = this.getNode(nodeInfo);
20026 if(!node || this.isSelected(node)){
20027 return; // already selected.
20030 this.clearSelections(true);
20033 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20034 Roo.fly(node).addClass(this.selectedClass);
20035 this.selections.push(node);
20036 if(!suppressEvent){
20037 this.fireEvent("selectionchange", this, this.selections);
20045 * @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
20046 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20047 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20049 unselect : function(nodeInfo, keepExisting, suppressEvent)
20051 if(nodeInfo instanceof Array){
20052 Roo.each(this.selections, function(s) {
20053 this.unselect(s, nodeInfo);
20057 var node = this.getNode(nodeInfo);
20058 if(!node || !this.isSelected(node)){
20059 //Roo.log("not selected");
20060 return; // not selected.
20064 Roo.each(this.selections, function(s) {
20066 Roo.fly(node).removeClass(this.selectedClass);
20073 this.selections= ns;
20074 this.fireEvent("selectionchange", this, this.selections);
20078 * Gets a template node.
20079 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20080 * @return {HTMLElement} The node or null if it wasn't found
20082 getNode : function(nodeInfo){
20083 if(typeof nodeInfo == "string"){
20084 return document.getElementById(nodeInfo);
20085 }else if(typeof nodeInfo == "number"){
20086 return this.nodes[nodeInfo];
20092 * Gets a range template nodes.
20093 * @param {Number} startIndex
20094 * @param {Number} endIndex
20095 * @return {Array} An array of nodes
20097 getNodes : function(start, end){
20098 var ns = this.nodes;
20099 start = start || 0;
20100 end = typeof end == "undefined" ? ns.length - 1 : end;
20103 for(var i = start; i <= end; i++){
20107 for(var i = start; i >= end; i--){
20115 * Finds the index of the passed node
20116 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20117 * @return {Number} The index of the node or -1
20119 indexOf : function(node){
20120 node = this.getNode(node);
20121 if(typeof node.nodeIndex == "number"){
20122 return node.nodeIndex;
20124 var ns = this.nodes;
20125 for(var i = 0, len = ns.length; i < len; i++){
20136 * based on jquery fullcalendar
20140 Roo.bootstrap = Roo.bootstrap || {};
20142 * @class Roo.bootstrap.Calendar
20143 * @extends Roo.bootstrap.Component
20144 * Bootstrap Calendar class
20145 * @cfg {Boolean} loadMask (true|false) default false
20146 * @cfg {Object} header generate the user specific header of the calendar, default false
20149 * Create a new Container
20150 * @param {Object} config The config object
20155 Roo.bootstrap.Calendar = function(config){
20156 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20160 * Fires when a date is selected
20161 * @param {DatePicker} this
20162 * @param {Date} date The selected date
20166 * @event monthchange
20167 * Fires when the displayed month changes
20168 * @param {DatePicker} this
20169 * @param {Date} date The selected month
20171 'monthchange': true,
20173 * @event evententer
20174 * Fires when mouse over an event
20175 * @param {Calendar} this
20176 * @param {event} Event
20178 'evententer': true,
20180 * @event eventleave
20181 * Fires when the mouse leaves an
20182 * @param {Calendar} this
20185 'eventleave': true,
20187 * @event eventclick
20188 * Fires when the mouse click an
20189 * @param {Calendar} this
20198 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20201 * @cfg {Number} startDay
20202 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20210 getAutoCreate : function(){
20213 var fc_button = function(name, corner, style, content ) {
20214 return Roo.apply({},{
20216 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20218 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20221 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20232 style : 'width:100%',
20239 cls : 'fc-header-left',
20241 fc_button('prev', 'left', 'arrow', '‹' ),
20242 fc_button('next', 'right', 'arrow', '›' ),
20243 { tag: 'span', cls: 'fc-header-space' },
20244 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20252 cls : 'fc-header-center',
20256 cls: 'fc-header-title',
20259 html : 'month / year'
20267 cls : 'fc-header-right',
20269 /* fc_button('month', 'left', '', 'month' ),
20270 fc_button('week', '', '', 'week' ),
20271 fc_button('day', 'right', '', 'day' )
20283 header = this.header;
20286 var cal_heads = function() {
20288 // fixme - handle this.
20290 for (var i =0; i < Date.dayNames.length; i++) {
20291 var d = Date.dayNames[i];
20294 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20295 html : d.substring(0,3)
20299 ret[0].cls += ' fc-first';
20300 ret[6].cls += ' fc-last';
20303 var cal_cell = function(n) {
20306 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20311 cls: 'fc-day-number',
20315 cls: 'fc-day-content',
20319 style: 'position: relative;' // height: 17px;
20331 var cal_rows = function() {
20334 for (var r = 0; r < 6; r++) {
20341 for (var i =0; i < Date.dayNames.length; i++) {
20342 var d = Date.dayNames[i];
20343 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20346 row.cn[0].cls+=' fc-first';
20347 row.cn[0].cn[0].style = 'min-height:90px';
20348 row.cn[6].cls+=' fc-last';
20352 ret[0].cls += ' fc-first';
20353 ret[4].cls += ' fc-prev-last';
20354 ret[5].cls += ' fc-last';
20361 cls: 'fc-border-separate',
20362 style : 'width:100%',
20370 cls : 'fc-first fc-last',
20388 cls : 'fc-content',
20389 style : "position: relative;",
20392 cls : 'fc-view fc-view-month fc-grid',
20393 style : 'position: relative',
20394 unselectable : 'on',
20397 cls : 'fc-event-container',
20398 style : 'position:absolute;z-index:8;top:0;left:0;'
20416 initEvents : function()
20419 throw "can not find store for calendar";
20425 style: "text-align:center",
20429 style: "background-color:white;width:50%;margin:250 auto",
20433 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20444 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20446 var size = this.el.select('.fc-content', true).first().getSize();
20447 this.maskEl.setSize(size.width, size.height);
20448 this.maskEl.enableDisplayMode("block");
20449 if(!this.loadMask){
20450 this.maskEl.hide();
20453 this.store = Roo.factory(this.store, Roo.data);
20454 this.store.on('load', this.onLoad, this);
20455 this.store.on('beforeload', this.onBeforeLoad, this);
20459 this.cells = this.el.select('.fc-day',true);
20460 //Roo.log(this.cells);
20461 this.textNodes = this.el.query('.fc-day-number');
20462 this.cells.addClassOnOver('fc-state-hover');
20464 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20465 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20466 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20467 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20469 this.on('monthchange', this.onMonthChange, this);
20471 this.update(new Date().clearTime());
20474 resize : function() {
20475 var sz = this.el.getSize();
20477 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20478 this.el.select('.fc-day-content div',true).setHeight(34);
20483 showPrevMonth : function(e){
20484 this.update(this.activeDate.add("mo", -1));
20486 showToday : function(e){
20487 this.update(new Date().clearTime());
20490 showNextMonth : function(e){
20491 this.update(this.activeDate.add("mo", 1));
20495 showPrevYear : function(){
20496 this.update(this.activeDate.add("y", -1));
20500 showNextYear : function(){
20501 this.update(this.activeDate.add("y", 1));
20506 update : function(date)
20508 var vd = this.activeDate;
20509 this.activeDate = date;
20510 // if(vd && this.el){
20511 // var t = date.getTime();
20512 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20513 // Roo.log('using add remove');
20515 // this.fireEvent('monthchange', this, date);
20517 // this.cells.removeClass("fc-state-highlight");
20518 // this.cells.each(function(c){
20519 // if(c.dateValue == t){
20520 // c.addClass("fc-state-highlight");
20521 // setTimeout(function(){
20522 // try{c.dom.firstChild.focus();}catch(e){}
20532 var days = date.getDaysInMonth();
20534 var firstOfMonth = date.getFirstDateOfMonth();
20535 var startingPos = firstOfMonth.getDay()-this.startDay;
20537 if(startingPos < this.startDay){
20541 var pm = date.add(Date.MONTH, -1);
20542 var prevStart = pm.getDaysInMonth()-startingPos;
20544 this.cells = this.el.select('.fc-day',true);
20545 this.textNodes = this.el.query('.fc-day-number');
20546 this.cells.addClassOnOver('fc-state-hover');
20548 var cells = this.cells.elements;
20549 var textEls = this.textNodes;
20551 Roo.each(cells, function(cell){
20552 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20555 days += startingPos;
20557 // convert everything to numbers so it's fast
20558 var day = 86400000;
20559 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20562 //Roo.log(prevStart);
20564 var today = new Date().clearTime().getTime();
20565 var sel = date.clearTime().getTime();
20566 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20567 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20568 var ddMatch = this.disabledDatesRE;
20569 var ddText = this.disabledDatesText;
20570 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20571 var ddaysText = this.disabledDaysText;
20572 var format = this.format;
20574 var setCellClass = function(cal, cell){
20578 //Roo.log('set Cell Class');
20580 var t = d.getTime();
20584 cell.dateValue = t;
20586 cell.className += " fc-today";
20587 cell.className += " fc-state-highlight";
20588 cell.title = cal.todayText;
20591 // disable highlight in other month..
20592 //cell.className += " fc-state-highlight";
20597 cell.className = " fc-state-disabled";
20598 cell.title = cal.minText;
20602 cell.className = " fc-state-disabled";
20603 cell.title = cal.maxText;
20607 if(ddays.indexOf(d.getDay()) != -1){
20608 cell.title = ddaysText;
20609 cell.className = " fc-state-disabled";
20612 if(ddMatch && format){
20613 var fvalue = d.dateFormat(format);
20614 if(ddMatch.test(fvalue)){
20615 cell.title = ddText.replace("%0", fvalue);
20616 cell.className = " fc-state-disabled";
20620 if (!cell.initialClassName) {
20621 cell.initialClassName = cell.dom.className;
20624 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20629 for(; i < startingPos; i++) {
20630 textEls[i].innerHTML = (++prevStart);
20631 d.setDate(d.getDate()+1);
20633 cells[i].className = "fc-past fc-other-month";
20634 setCellClass(this, cells[i]);
20639 for(; i < days; i++){
20640 intDay = i - startingPos + 1;
20641 textEls[i].innerHTML = (intDay);
20642 d.setDate(d.getDate()+1);
20644 cells[i].className = ''; // "x-date-active";
20645 setCellClass(this, cells[i]);
20649 for(; i < 42; i++) {
20650 textEls[i].innerHTML = (++extraDays);
20651 d.setDate(d.getDate()+1);
20653 cells[i].className = "fc-future fc-other-month";
20654 setCellClass(this, cells[i]);
20657 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20659 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20661 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20662 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20664 if(totalRows != 6){
20665 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20666 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20669 this.fireEvent('monthchange', this, date);
20673 if(!this.internalRender){
20674 var main = this.el.dom.firstChild;
20675 var w = main.offsetWidth;
20676 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20677 Roo.fly(main).setWidth(w);
20678 this.internalRender = true;
20679 // opera does not respect the auto grow header center column
20680 // then, after it gets a width opera refuses to recalculate
20681 // without a second pass
20682 if(Roo.isOpera && !this.secondPass){
20683 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20684 this.secondPass = true;
20685 this.update.defer(10, this, [date]);
20692 findCell : function(dt) {
20693 dt = dt.clearTime().getTime();
20695 this.cells.each(function(c){
20696 //Roo.log("check " +c.dateValue + '?=' + dt);
20697 if(c.dateValue == dt){
20707 findCells : function(ev) {
20708 var s = ev.start.clone().clearTime().getTime();
20710 var e= ev.end.clone().clearTime().getTime();
20713 this.cells.each(function(c){
20714 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20716 if(c.dateValue > e){
20719 if(c.dateValue < s){
20728 // findBestRow: function(cells)
20732 // for (var i =0 ; i < cells.length;i++) {
20733 // ret = Math.max(cells[i].rows || 0,ret);
20740 addItem : function(ev)
20742 // look for vertical location slot in
20743 var cells = this.findCells(ev);
20745 // ev.row = this.findBestRow(cells);
20747 // work out the location.
20751 for(var i =0; i < cells.length; i++) {
20753 cells[i].row = cells[0].row;
20756 cells[i].row = cells[i].row + 1;
20766 if (crow.start.getY() == cells[i].getY()) {
20768 crow.end = cells[i];
20785 cells[0].events.push(ev);
20787 this.calevents.push(ev);
20790 clearEvents: function() {
20792 if(!this.calevents){
20796 Roo.each(this.cells.elements, function(c){
20802 Roo.each(this.calevents, function(e) {
20803 Roo.each(e.els, function(el) {
20804 el.un('mouseenter' ,this.onEventEnter, this);
20805 el.un('mouseleave' ,this.onEventLeave, this);
20810 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20816 renderEvents: function()
20820 this.cells.each(function(c) {
20829 if(c.row != c.events.length){
20830 r = 4 - (4 - (c.row - c.events.length));
20833 c.events = ev.slice(0, r);
20834 c.more = ev.slice(r);
20836 if(c.more.length && c.more.length == 1){
20837 c.events.push(c.more.pop());
20840 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20844 this.cells.each(function(c) {
20846 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20849 for (var e = 0; e < c.events.length; e++){
20850 var ev = c.events[e];
20851 var rows = ev.rows;
20853 for(var i = 0; i < rows.length; i++) {
20855 // how many rows should it span..
20858 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20859 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20861 unselectable : "on",
20864 cls: 'fc-event-inner',
20868 // cls: 'fc-event-time',
20869 // html : cells.length > 1 ? '' : ev.time
20873 cls: 'fc-event-title',
20874 html : String.format('{0}', ev.title)
20881 cls: 'ui-resizable-handle ui-resizable-e',
20882 html : '  '
20889 cfg.cls += ' fc-event-start';
20891 if ((i+1) == rows.length) {
20892 cfg.cls += ' fc-event-end';
20895 var ctr = _this.el.select('.fc-event-container',true).first();
20896 var cg = ctr.createChild(cfg);
20898 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20899 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20901 var r = (c.more.length) ? 1 : 0;
20902 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20903 cg.setWidth(ebox.right - sbox.x -2);
20905 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20906 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20907 cg.on('click', _this.onEventClick, _this, ev);
20918 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20919 style : 'position: absolute',
20920 unselectable : "on",
20923 cls: 'fc-event-inner',
20927 cls: 'fc-event-title',
20935 cls: 'ui-resizable-handle ui-resizable-e',
20936 html : '  '
20942 var ctr = _this.el.select('.fc-event-container',true).first();
20943 var cg = ctr.createChild(cfg);
20945 var sbox = c.select('.fc-day-content',true).first().getBox();
20946 var ebox = c.select('.fc-day-content',true).first().getBox();
20948 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20949 cg.setWidth(ebox.right - sbox.x -2);
20951 cg.on('click', _this.onMoreEventClick, _this, c.more);
20961 onEventEnter: function (e, el,event,d) {
20962 this.fireEvent('evententer', this, el, event);
20965 onEventLeave: function (e, el,event,d) {
20966 this.fireEvent('eventleave', this, el, event);
20969 onEventClick: function (e, el,event,d) {
20970 this.fireEvent('eventclick', this, el, event);
20973 onMonthChange: function () {
20977 onMoreEventClick: function(e, el, more)
20981 this.calpopover.placement = 'right';
20982 this.calpopover.setTitle('More');
20984 this.calpopover.setContent('');
20986 var ctr = this.calpopover.el.select('.popover-content', true).first();
20988 Roo.each(more, function(m){
20990 cls : 'fc-event-hori fc-event-draggable',
20993 var cg = ctr.createChild(cfg);
20995 cg.on('click', _this.onEventClick, _this, m);
20998 this.calpopover.show(el);
21003 onLoad: function ()
21005 this.calevents = [];
21008 if(this.store.getCount() > 0){
21009 this.store.data.each(function(d){
21012 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21013 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21014 time : d.data.start_time,
21015 title : d.data.title,
21016 description : d.data.description,
21017 venue : d.data.venue
21022 this.renderEvents();
21024 if(this.calevents.length && this.loadMask){
21025 this.maskEl.hide();
21029 onBeforeLoad: function()
21031 this.clearEvents();
21033 this.maskEl.show();
21047 * @class Roo.bootstrap.Popover
21048 * @extends Roo.bootstrap.Component
21049 * Bootstrap Popover class
21050 * @cfg {String} html contents of the popover (or false to use children..)
21051 * @cfg {String} title of popover (or false to hide)
21052 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21053 * @cfg {String} trigger click || hover (or false to trigger manually)
21054 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21055 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21056 * - if false and it has a 'parent' then it will be automatically added to that element
21057 * - if string - Roo.get will be called
21058 * @cfg {Number} delay - delay before showing
21061 * Create a new Popover
21062 * @param {Object} config The config object
21065 Roo.bootstrap.Popover = function(config){
21066 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21072 * After the popover show
21074 * @param {Roo.bootstrap.Popover} this
21079 * After the popover hide
21081 * @param {Roo.bootstrap.Popover} this
21087 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21092 placement : 'right',
21093 trigger : 'hover', // hover
21099 can_build_overlaid : false,
21101 maskEl : false, // the mask element
21104 alignEl : false, // when show is called with an element - this get's stored.
21106 getChildContainer : function()
21108 return this.contentEl;
21111 getPopoverHeader : function()
21113 this.title = true; // flag not to hide it..
21114 this.headerEl.addClass('p-0');
21115 return this.headerEl
21119 getAutoCreate : function(){
21122 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21123 style: 'display:block',
21129 cls : 'popover-inner ',
21133 cls: 'popover-title popover-header',
21134 html : this.title === false ? '' : this.title
21137 cls : 'popover-content popover-body ' + (this.cls || ''),
21138 html : this.html || ''
21149 * @param {string} the title
21151 setTitle: function(str)
21155 this.headerEl.dom.innerHTML = str;
21160 * @param {string} the body content
21162 setContent: function(str)
21165 if (this.contentEl) {
21166 this.contentEl.dom.innerHTML = str;
21170 // as it get's added to the bottom of the page.
21171 onRender : function(ct, position)
21173 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21178 var cfg = Roo.apply({}, this.getAutoCreate());
21182 cfg.cls += ' ' + this.cls;
21185 cfg.style = this.style;
21187 //Roo.log("adding to ");
21188 this.el = Roo.get(document.body).createChild(cfg, position);
21189 // Roo.log(this.el);
21192 this.contentEl = this.el.select('.popover-content',true).first();
21193 this.headerEl = this.el.select('.popover-title',true).first();
21196 if(typeof(this.items) != 'undefined'){
21197 var items = this.items;
21200 for(var i =0;i < items.length;i++) {
21201 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21205 this.items = nitems;
21207 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21208 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21215 resizeMask : function()
21217 this.maskEl.setSize(
21218 Roo.lib.Dom.getViewWidth(true),
21219 Roo.lib.Dom.getViewHeight(true)
21223 initEvents : function()
21227 Roo.bootstrap.Popover.register(this);
21230 this.arrowEl = this.el.select('.arrow',true).first();
21231 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21232 this.el.enableDisplayMode('block');
21236 if (this.over === false && !this.parent()) {
21239 if (this.triggers === false) {
21244 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21245 var triggers = this.trigger ? this.trigger.split(' ') : [];
21246 Roo.each(triggers, function(trigger) {
21248 if (trigger == 'click') {
21249 on_el.on('click', this.toggle, this);
21250 } else if (trigger != 'manual') {
21251 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21252 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21254 on_el.on(eventIn ,this.enter, this);
21255 on_el.on(eventOut, this.leave, this);
21265 toggle : function () {
21266 this.hoverState == 'in' ? this.leave() : this.enter();
21269 enter : function () {
21271 clearTimeout(this.timeout);
21273 this.hoverState = 'in';
21275 if (!this.delay || !this.delay.show) {
21280 this.timeout = setTimeout(function () {
21281 if (_t.hoverState == 'in') {
21284 }, this.delay.show)
21287 leave : function() {
21288 clearTimeout(this.timeout);
21290 this.hoverState = 'out';
21292 if (!this.delay || !this.delay.hide) {
21297 this.timeout = setTimeout(function () {
21298 if (_t.hoverState == 'out') {
21301 }, this.delay.hide)
21305 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21306 * @param {string} (left|right|top|bottom) position
21308 show : function (on_el, placement)
21310 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21311 on_el = on_el || false; // default to false
21314 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21315 on_el = this.parent().el;
21316 } else if (this.over) {
21317 on_el = Roo.get(this.over);
21322 this.alignEl = Roo.get( on_el );
21325 this.render(document.body);
21331 if (this.title === false) {
21332 this.headerEl.hide();
21337 this.el.dom.style.display = 'block';
21340 if (this.alignEl) {
21341 this.updatePosition(this.placement, true);
21344 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21345 var es = this.el.getSize();
21346 var x = Roo.lib.Dom.getViewWidth()/2;
21347 var y = Roo.lib.Dom.getViewHeight()/2;
21348 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21353 //var arrow = this.el.select('.arrow',true).first();
21354 //arrow.set(align[2],
21356 this.el.addClass('in');
21360 this.hoverState = 'in';
21363 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21364 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21365 this.maskEl.dom.style.display = 'block';
21366 this.maskEl.addClass('show');
21368 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21370 this.fireEvent('show', this);
21374 * fire this manually after loading a grid in the table for example
21375 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21376 * @param {Boolean} try and move it if we cant get right position.
21378 updatePosition : function(placement, try_move)
21380 // allow for calling with no parameters
21381 placement = placement ? placement : this.placement;
21382 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21384 this.el.removeClass([
21385 'fade','top','bottom', 'left', 'right','in',
21386 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21388 this.el.addClass(placement + ' bs-popover-' + placement);
21390 if (!this.alignEl ) {
21394 switch (placement) {
21396 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21397 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21398 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21399 //normal display... or moved up/down.
21400 this.el.setXY(offset);
21401 var xy = this.alignEl.getAnchorXY('tr', false);
21403 this.arrowEl.setXY(xy);
21406 // continue through...
21407 return this.updatePosition('left', false);
21411 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21412 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21413 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21414 //normal display... or moved up/down.
21415 this.el.setXY(offset);
21416 var xy = this.alignEl.getAnchorXY('tl', false);
21417 xy[0]-=10;xy[1]+=5; // << fix me
21418 this.arrowEl.setXY(xy);
21422 return this.updatePosition('right', false);
21425 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21426 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21427 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21428 //normal display... or moved up/down.
21429 this.el.setXY(offset);
21430 var xy = this.alignEl.getAnchorXY('t', false);
21431 xy[1]-=10; // << fix me
21432 this.arrowEl.setXY(xy);
21436 return this.updatePosition('bottom', false);
21439 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21440 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21441 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21442 //normal display... or moved up/down.
21443 this.el.setXY(offset);
21444 var xy = this.alignEl.getAnchorXY('b', false);
21445 xy[1]+=2; // << fix me
21446 this.arrowEl.setXY(xy);
21450 return this.updatePosition('top', false);
21461 this.el.setXY([0,0]);
21462 this.el.removeClass('in');
21464 this.hoverState = null;
21465 this.maskEl.hide(); // always..
21466 this.fireEvent('hide', this);
21472 Roo.apply(Roo.bootstrap.Popover, {
21475 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21476 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21477 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21478 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21483 clickHander : false,
21487 onMouseDown : function(e)
21489 if (this.popups.length && !e.getTarget(".roo-popover")) {
21490 /// what is nothing is showing..
21499 register : function(popup)
21501 if (!Roo.bootstrap.Popover.clickHandler) {
21502 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21504 // hide other popups.
21505 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21506 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21507 this.hideAll(); //<< why?
21508 //this.popups.push(popup);
21510 hideAll : function()
21512 this.popups.forEach(function(p) {
21516 onShow : function() {
21517 Roo.bootstrap.Popover.popups.push(this);
21519 onHide : function() {
21520 Roo.bootstrap.Popover.popups.remove(this);
21526 * Card header - holder for the card header elements.
21531 * @class Roo.bootstrap.PopoverNav
21532 * @extends Roo.bootstrap.NavGroup
21533 * Bootstrap Popover header navigation class
21535 * Create a new Popover Header Navigation
21536 * @param {Object} config The config object
21539 Roo.bootstrap.PopoverNav = function(config){
21540 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21543 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21546 container_method : 'getPopoverHeader'
21564 * @class Roo.bootstrap.Progress
21565 * @extends Roo.bootstrap.Component
21566 * Bootstrap Progress class
21567 * @cfg {Boolean} striped striped of the progress bar
21568 * @cfg {Boolean} active animated of the progress bar
21572 * Create a new Progress
21573 * @param {Object} config The config object
21576 Roo.bootstrap.Progress = function(config){
21577 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21580 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21585 getAutoCreate : function(){
21593 cfg.cls += ' progress-striped';
21597 cfg.cls += ' active';
21616 * @class Roo.bootstrap.ProgressBar
21617 * @extends Roo.bootstrap.Component
21618 * Bootstrap ProgressBar class
21619 * @cfg {Number} aria_valuenow aria-value now
21620 * @cfg {Number} aria_valuemin aria-value min
21621 * @cfg {Number} aria_valuemax aria-value max
21622 * @cfg {String} label label for the progress bar
21623 * @cfg {String} panel (success | info | warning | danger )
21624 * @cfg {String} role role of the progress bar
21625 * @cfg {String} sr_only text
21629 * Create a new ProgressBar
21630 * @param {Object} config The config object
21633 Roo.bootstrap.ProgressBar = function(config){
21634 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21637 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21641 aria_valuemax : 100,
21647 getAutoCreate : function()
21652 cls: 'progress-bar',
21653 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21665 cfg.role = this.role;
21668 if(this.aria_valuenow){
21669 cfg['aria-valuenow'] = this.aria_valuenow;
21672 if(this.aria_valuemin){
21673 cfg['aria-valuemin'] = this.aria_valuemin;
21676 if(this.aria_valuemax){
21677 cfg['aria-valuemax'] = this.aria_valuemax;
21680 if(this.label && !this.sr_only){
21681 cfg.html = this.label;
21685 cfg.cls += ' progress-bar-' + this.panel;
21691 update : function(aria_valuenow)
21693 this.aria_valuenow = aria_valuenow;
21695 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21710 * @class Roo.bootstrap.TabGroup
21711 * @extends Roo.bootstrap.Column
21712 * Bootstrap Column class
21713 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21714 * @cfg {Boolean} carousel true to make the group behave like a carousel
21715 * @cfg {Boolean} bullets show bullets for the panels
21716 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21717 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21718 * @cfg {Boolean} showarrow (true|false) show arrow default true
21721 * Create a new TabGroup
21722 * @param {Object} config The config object
21725 Roo.bootstrap.TabGroup = function(config){
21726 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21728 this.navId = Roo.id();
21731 Roo.bootstrap.TabGroup.register(this);
21735 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21738 transition : false,
21743 slideOnTouch : false,
21746 getAutoCreate : function()
21748 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21750 cfg.cls += ' tab-content';
21752 if (this.carousel) {
21753 cfg.cls += ' carousel slide';
21756 cls : 'carousel-inner',
21760 if(this.bullets && !Roo.isTouch){
21763 cls : 'carousel-bullets',
21767 if(this.bullets_cls){
21768 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21775 cfg.cn[0].cn.push(bullets);
21778 if(this.showarrow){
21779 cfg.cn[0].cn.push({
21781 class : 'carousel-arrow',
21785 class : 'carousel-prev',
21789 class : 'fa fa-chevron-left'
21795 class : 'carousel-next',
21799 class : 'fa fa-chevron-right'
21812 initEvents: function()
21814 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21815 // this.el.on("touchstart", this.onTouchStart, this);
21818 if(this.autoslide){
21821 this.slideFn = window.setInterval(function() {
21822 _this.showPanelNext();
21826 if(this.showarrow){
21827 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21828 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21834 // onTouchStart : function(e, el, o)
21836 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21840 // this.showPanelNext();
21844 getChildContainer : function()
21846 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21850 * register a Navigation item
21851 * @param {Roo.bootstrap.NavItem} the navitem to add
21853 register : function(item)
21855 this.tabs.push( item);
21856 item.navId = this.navId; // not really needed..
21861 getActivePanel : function()
21864 Roo.each(this.tabs, function(t) {
21874 getPanelByName : function(n)
21877 Roo.each(this.tabs, function(t) {
21878 if (t.tabId == n) {
21886 indexOfPanel : function(p)
21889 Roo.each(this.tabs, function(t,i) {
21890 if (t.tabId == p.tabId) {
21899 * show a specific panel
21900 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21901 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21903 showPanel : function (pan)
21905 if(this.transition || typeof(pan) == 'undefined'){
21906 Roo.log("waiting for the transitionend");
21910 if (typeof(pan) == 'number') {
21911 pan = this.tabs[pan];
21914 if (typeof(pan) == 'string') {
21915 pan = this.getPanelByName(pan);
21918 var cur = this.getActivePanel();
21921 Roo.log('pan or acitve pan is undefined');
21925 if (pan.tabId == this.getActivePanel().tabId) {
21929 if (false === cur.fireEvent('beforedeactivate')) {
21933 if(this.bullets > 0 && !Roo.isTouch){
21934 this.setActiveBullet(this.indexOfPanel(pan));
21937 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21939 //class="carousel-item carousel-item-next carousel-item-left"
21941 this.transition = true;
21942 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21943 var lr = dir == 'next' ? 'left' : 'right';
21944 pan.el.addClass(dir); // or prev
21945 pan.el.addClass('carousel-item-' + dir); // or prev
21946 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21947 cur.el.addClass(lr); // or right
21948 pan.el.addClass(lr);
21949 cur.el.addClass('carousel-item-' +lr); // or right
21950 pan.el.addClass('carousel-item-' +lr);
21954 cur.el.on('transitionend', function() {
21955 Roo.log("trans end?");
21957 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21958 pan.setActive(true);
21960 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21961 cur.setActive(false);
21963 _this.transition = false;
21965 }, this, { single: true } );
21970 cur.setActive(false);
21971 pan.setActive(true);
21976 showPanelNext : function()
21978 var i = this.indexOfPanel(this.getActivePanel());
21980 if (i >= this.tabs.length - 1 && !this.autoslide) {
21984 if (i >= this.tabs.length - 1 && this.autoslide) {
21988 this.showPanel(this.tabs[i+1]);
21991 showPanelPrev : function()
21993 var i = this.indexOfPanel(this.getActivePanel());
21995 if (i < 1 && !this.autoslide) {
21999 if (i < 1 && this.autoslide) {
22000 i = this.tabs.length;
22003 this.showPanel(this.tabs[i-1]);
22007 addBullet: function()
22009 if(!this.bullets || Roo.isTouch){
22012 var ctr = this.el.select('.carousel-bullets',true).first();
22013 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22014 var bullet = ctr.createChild({
22015 cls : 'bullet bullet-' + i
22016 },ctr.dom.lastChild);
22021 bullet.on('click', (function(e, el, o, ii, t){
22023 e.preventDefault();
22025 this.showPanel(ii);
22027 if(this.autoslide && this.slideFn){
22028 clearInterval(this.slideFn);
22029 this.slideFn = window.setInterval(function() {
22030 _this.showPanelNext();
22034 }).createDelegate(this, [i, bullet], true));
22039 setActiveBullet : function(i)
22045 Roo.each(this.el.select('.bullet', true).elements, function(el){
22046 el.removeClass('selected');
22049 var bullet = this.el.select('.bullet-' + i, true).first();
22055 bullet.addClass('selected');
22066 Roo.apply(Roo.bootstrap.TabGroup, {
22070 * register a Navigation Group
22071 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22073 register : function(navgrp)
22075 this.groups[navgrp.navId] = navgrp;
22079 * fetch a Navigation Group based on the navigation ID
22080 * if one does not exist , it will get created.
22081 * @param {string} the navgroup to add
22082 * @returns {Roo.bootstrap.NavGroup} the navgroup
22084 get: function(navId) {
22085 if (typeof(this.groups[navId]) == 'undefined') {
22086 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22088 return this.groups[navId] ;
22103 * @class Roo.bootstrap.TabPanel
22104 * @extends Roo.bootstrap.Component
22105 * Bootstrap TabPanel class
22106 * @cfg {Boolean} active panel active
22107 * @cfg {String} html panel content
22108 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22109 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22110 * @cfg {String} href click to link..
22111 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22115 * Create a new TabPanel
22116 * @param {Object} config The config object
22119 Roo.bootstrap.TabPanel = function(config){
22120 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22124 * Fires when the active status changes
22125 * @param {Roo.bootstrap.TabPanel} this
22126 * @param {Boolean} state the new state
22131 * @event beforedeactivate
22132 * Fires before a tab is de-activated - can be used to do validation on a form.
22133 * @param {Roo.bootstrap.TabPanel} this
22134 * @return {Boolean} false if there is an error
22137 'beforedeactivate': true
22140 this.tabId = this.tabId || Roo.id();
22144 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22151 touchSlide : false,
22152 getAutoCreate : function(){
22157 // item is needed for carousel - not sure if it has any effect otherwise
22158 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22159 html: this.html || ''
22163 cfg.cls += ' active';
22167 cfg.tabId = this.tabId;
22175 initEvents: function()
22177 var p = this.parent();
22179 this.navId = this.navId || p.navId;
22181 if (typeof(this.navId) != 'undefined') {
22182 // not really needed.. but just in case.. parent should be a NavGroup.
22183 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22187 var i = tg.tabs.length - 1;
22189 if(this.active && tg.bullets > 0 && i < tg.bullets){
22190 tg.setActiveBullet(i);
22194 this.el.on('click', this.onClick, this);
22196 if(Roo.isTouch && this.touchSlide){
22197 this.el.on("touchstart", this.onTouchStart, this);
22198 this.el.on("touchmove", this.onTouchMove, this);
22199 this.el.on("touchend", this.onTouchEnd, this);
22204 onRender : function(ct, position)
22206 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22209 setActive : function(state)
22211 Roo.log("panel - set active " + this.tabId + "=" + state);
22213 this.active = state;
22215 this.el.removeClass('active');
22217 } else if (!this.el.hasClass('active')) {
22218 this.el.addClass('active');
22221 this.fireEvent('changed', this, state);
22224 onClick : function(e)
22226 e.preventDefault();
22228 if(!this.href.length){
22232 window.location.href = this.href;
22241 onTouchStart : function(e)
22243 this.swiping = false;
22245 this.startX = e.browserEvent.touches[0].clientX;
22246 this.startY = e.browserEvent.touches[0].clientY;
22249 onTouchMove : function(e)
22251 this.swiping = true;
22253 this.endX = e.browserEvent.touches[0].clientX;
22254 this.endY = e.browserEvent.touches[0].clientY;
22257 onTouchEnd : function(e)
22264 var tabGroup = this.parent();
22266 if(this.endX > this.startX){ // swiping right
22267 tabGroup.showPanelPrev();
22271 if(this.startX > this.endX){ // swiping left
22272 tabGroup.showPanelNext();
22291 * @class Roo.bootstrap.DateField
22292 * @extends Roo.bootstrap.Input
22293 * Bootstrap DateField class
22294 * @cfg {Number} weekStart default 0
22295 * @cfg {String} viewMode default empty, (months|years)
22296 * @cfg {String} minViewMode default empty, (months|years)
22297 * @cfg {Number} startDate default -Infinity
22298 * @cfg {Number} endDate default Infinity
22299 * @cfg {Boolean} todayHighlight default false
22300 * @cfg {Boolean} todayBtn default false
22301 * @cfg {Boolean} calendarWeeks default false
22302 * @cfg {Object} daysOfWeekDisabled default empty
22303 * @cfg {Boolean} singleMode default false (true | false)
22305 * @cfg {Boolean} keyboardNavigation default true
22306 * @cfg {String} language default en
22309 * Create a new DateField
22310 * @param {Object} config The config object
22313 Roo.bootstrap.DateField = function(config){
22314 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22318 * Fires when this field show.
22319 * @param {Roo.bootstrap.DateField} this
22320 * @param {Mixed} date The date value
22325 * Fires when this field hide.
22326 * @param {Roo.bootstrap.DateField} this
22327 * @param {Mixed} date The date value
22332 * Fires when select a date.
22333 * @param {Roo.bootstrap.DateField} this
22334 * @param {Mixed} date The date value
22338 * @event beforeselect
22339 * Fires when before select a date.
22340 * @param {Roo.bootstrap.DateField} this
22341 * @param {Mixed} date The date value
22343 beforeselect : true
22347 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22350 * @cfg {String} format
22351 * The default date format string which can be overriden for localization support. The format must be
22352 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22356 * @cfg {String} altFormats
22357 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22358 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22360 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22368 todayHighlight : false,
22374 keyboardNavigation: true,
22376 calendarWeeks: false,
22378 startDate: -Infinity,
22382 daysOfWeekDisabled: [],
22386 singleMode : false,
22388 UTCDate: function()
22390 return new Date(Date.UTC.apply(Date, arguments));
22393 UTCToday: function()
22395 var today = new Date();
22396 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22399 getDate: function() {
22400 var d = this.getUTCDate();
22401 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22404 getUTCDate: function() {
22408 setDate: function(d) {
22409 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22412 setUTCDate: function(d) {
22414 this.setValue(this.formatDate(this.date));
22417 onRender: function(ct, position)
22420 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22422 this.language = this.language || 'en';
22423 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22424 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22426 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22427 this.format = this.format || 'm/d/y';
22428 this.isInline = false;
22429 this.isInput = true;
22430 this.component = this.el.select('.add-on', true).first() || false;
22431 this.component = (this.component && this.component.length === 0) ? false : this.component;
22432 this.hasInput = this.component && this.inputEl().length;
22434 if (typeof(this.minViewMode === 'string')) {
22435 switch (this.minViewMode) {
22437 this.minViewMode = 1;
22440 this.minViewMode = 2;
22443 this.minViewMode = 0;
22448 if (typeof(this.viewMode === 'string')) {
22449 switch (this.viewMode) {
22462 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22464 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22466 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22468 this.picker().on('mousedown', this.onMousedown, this);
22469 this.picker().on('click', this.onClick, this);
22471 this.picker().addClass('datepicker-dropdown');
22473 this.startViewMode = this.viewMode;
22475 if(this.singleMode){
22476 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22477 v.setVisibilityMode(Roo.Element.DISPLAY);
22481 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22482 v.setStyle('width', '189px');
22486 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22487 if(!this.calendarWeeks){
22492 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22493 v.attr('colspan', function(i, val){
22494 return parseInt(val) + 1;
22499 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22501 this.setStartDate(this.startDate);
22502 this.setEndDate(this.endDate);
22504 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22511 if(this.isInline) {
22516 picker : function()
22518 return this.pickerEl;
22519 // return this.el.select('.datepicker', true).first();
22522 fillDow: function()
22524 var dowCnt = this.weekStart;
22533 if(this.calendarWeeks){
22541 while (dowCnt < this.weekStart + 7) {
22545 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22549 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22552 fillMonths: function()
22555 var months = this.picker().select('>.datepicker-months td', true).first();
22557 months.dom.innerHTML = '';
22563 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22566 months.createChild(month);
22573 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;
22575 if (this.date < this.startDate) {
22576 this.viewDate = new Date(this.startDate);
22577 } else if (this.date > this.endDate) {
22578 this.viewDate = new Date(this.endDate);
22580 this.viewDate = new Date(this.date);
22588 var d = new Date(this.viewDate),
22589 year = d.getUTCFullYear(),
22590 month = d.getUTCMonth(),
22591 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22592 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22593 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22594 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22595 currentDate = this.date && this.date.valueOf(),
22596 today = this.UTCToday();
22598 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22600 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22602 // this.picker.select('>tfoot th.today').
22603 // .text(dates[this.language].today)
22604 // .toggle(this.todayBtn !== false);
22606 this.updateNavArrows();
22609 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22611 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22613 prevMonth.setUTCDate(day);
22615 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22617 var nextMonth = new Date(prevMonth);
22619 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22621 nextMonth = nextMonth.valueOf();
22623 var fillMonths = false;
22625 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22627 while(prevMonth.valueOf() <= nextMonth) {
22630 if (prevMonth.getUTCDay() === this.weekStart) {
22632 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22640 if(this.calendarWeeks){
22641 // ISO 8601: First week contains first thursday.
22642 // ISO also states week starts on Monday, but we can be more abstract here.
22644 // Start of current week: based on weekstart/current date
22645 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22646 // Thursday of this week
22647 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22648 // First Thursday of year, year from thursday
22649 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22650 // Calendar week: ms between thursdays, div ms per day, div 7 days
22651 calWeek = (th - yth) / 864e5 / 7 + 1;
22653 fillMonths.cn.push({
22661 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22663 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22666 if (this.todayHighlight &&
22667 prevMonth.getUTCFullYear() == today.getFullYear() &&
22668 prevMonth.getUTCMonth() == today.getMonth() &&
22669 prevMonth.getUTCDate() == today.getDate()) {
22670 clsName += ' today';
22673 if (currentDate && prevMonth.valueOf() === currentDate) {
22674 clsName += ' active';
22677 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22678 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22679 clsName += ' disabled';
22682 fillMonths.cn.push({
22684 cls: 'day ' + clsName,
22685 html: prevMonth.getDate()
22688 prevMonth.setDate(prevMonth.getDate()+1);
22691 var currentYear = this.date && this.date.getUTCFullYear();
22692 var currentMonth = this.date && this.date.getUTCMonth();
22694 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22696 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22697 v.removeClass('active');
22699 if(currentYear === year && k === currentMonth){
22700 v.addClass('active');
22703 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22704 v.addClass('disabled');
22710 year = parseInt(year/10, 10) * 10;
22712 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22714 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22717 for (var i = -1; i < 11; i++) {
22718 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22720 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22728 showMode: function(dir)
22731 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22734 Roo.each(this.picker().select('>div',true).elements, function(v){
22735 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22738 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22743 if(this.isInline) {
22747 this.picker().removeClass(['bottom', 'top']);
22749 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22751 * place to the top of element!
22755 this.picker().addClass('top');
22756 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22761 this.picker().addClass('bottom');
22763 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22766 parseDate : function(value)
22768 if(!value || value instanceof Date){
22771 var v = Date.parseDate(value, this.format);
22772 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22773 v = Date.parseDate(value, 'Y-m-d');
22775 if(!v && this.altFormats){
22776 if(!this.altFormatsArray){
22777 this.altFormatsArray = this.altFormats.split("|");
22779 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22780 v = Date.parseDate(value, this.altFormatsArray[i]);
22786 formatDate : function(date, fmt)
22788 return (!date || !(date instanceof Date)) ?
22789 date : date.dateFormat(fmt || this.format);
22792 onFocus : function()
22794 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22798 onBlur : function()
22800 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22802 var d = this.inputEl().getValue();
22809 showPopup : function()
22811 this.picker().show();
22815 this.fireEvent('showpopup', this, this.date);
22818 hidePopup : function()
22820 if(this.isInline) {
22823 this.picker().hide();
22824 this.viewMode = this.startViewMode;
22827 this.fireEvent('hidepopup', this, this.date);
22831 onMousedown: function(e)
22833 e.stopPropagation();
22834 e.preventDefault();
22839 Roo.bootstrap.DateField.superclass.keyup.call(this);
22843 setValue: function(v)
22845 if(this.fireEvent('beforeselect', this, v) !== false){
22846 var d = new Date(this.parseDate(v) ).clearTime();
22848 if(isNaN(d.getTime())){
22849 this.date = this.viewDate = '';
22850 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22854 v = this.formatDate(d);
22856 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22858 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22862 this.fireEvent('select', this, this.date);
22866 getValue: function()
22868 return this.formatDate(this.date);
22871 fireKey: function(e)
22873 if (!this.picker().isVisible()){
22874 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22880 var dateChanged = false,
22882 newDate, newViewDate;
22887 e.preventDefault();
22891 if (!this.keyboardNavigation) {
22894 dir = e.keyCode == 37 ? -1 : 1;
22897 newDate = this.moveYear(this.date, dir);
22898 newViewDate = this.moveYear(this.viewDate, dir);
22899 } else if (e.shiftKey){
22900 newDate = this.moveMonth(this.date, dir);
22901 newViewDate = this.moveMonth(this.viewDate, dir);
22903 newDate = new Date(this.date);
22904 newDate.setUTCDate(this.date.getUTCDate() + dir);
22905 newViewDate = new Date(this.viewDate);
22906 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22908 if (this.dateWithinRange(newDate)){
22909 this.date = newDate;
22910 this.viewDate = newViewDate;
22911 this.setValue(this.formatDate(this.date));
22913 e.preventDefault();
22914 dateChanged = true;
22919 if (!this.keyboardNavigation) {
22922 dir = e.keyCode == 38 ? -1 : 1;
22924 newDate = this.moveYear(this.date, dir);
22925 newViewDate = this.moveYear(this.viewDate, dir);
22926 } else if (e.shiftKey){
22927 newDate = this.moveMonth(this.date, dir);
22928 newViewDate = this.moveMonth(this.viewDate, dir);
22930 newDate = new Date(this.date);
22931 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22932 newViewDate = new Date(this.viewDate);
22933 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22935 if (this.dateWithinRange(newDate)){
22936 this.date = newDate;
22937 this.viewDate = newViewDate;
22938 this.setValue(this.formatDate(this.date));
22940 e.preventDefault();
22941 dateChanged = true;
22945 this.setValue(this.formatDate(this.date));
22947 e.preventDefault();
22950 this.setValue(this.formatDate(this.date));
22964 onClick: function(e)
22966 e.stopPropagation();
22967 e.preventDefault();
22969 var target = e.getTarget();
22971 if(target.nodeName.toLowerCase() === 'i'){
22972 target = Roo.get(target).dom.parentNode;
22975 var nodeName = target.nodeName;
22976 var className = target.className;
22977 var html = target.innerHTML;
22978 //Roo.log(nodeName);
22980 switch(nodeName.toLowerCase()) {
22982 switch(className) {
22988 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22989 switch(this.viewMode){
22991 this.viewDate = this.moveMonth(this.viewDate, dir);
22995 this.viewDate = this.moveYear(this.viewDate, dir);
23001 var date = new Date();
23002 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23004 this.setValue(this.formatDate(this.date));
23011 if (className.indexOf('disabled') < 0) {
23012 if (!this.viewDate) {
23013 this.viewDate = new Date();
23015 this.viewDate.setUTCDate(1);
23016 if (className.indexOf('month') > -1) {
23017 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23019 var year = parseInt(html, 10) || 0;
23020 this.viewDate.setUTCFullYear(year);
23024 if(this.singleMode){
23025 this.setValue(this.formatDate(this.viewDate));
23036 //Roo.log(className);
23037 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23038 var day = parseInt(html, 10) || 1;
23039 var year = (this.viewDate || new Date()).getUTCFullYear(),
23040 month = (this.viewDate || new Date()).getUTCMonth();
23042 if (className.indexOf('old') > -1) {
23049 } else if (className.indexOf('new') > -1) {
23057 //Roo.log([year,month,day]);
23058 this.date = this.UTCDate(year, month, day,0,0,0,0);
23059 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23061 //Roo.log(this.formatDate(this.date));
23062 this.setValue(this.formatDate(this.date));
23069 setStartDate: function(startDate)
23071 this.startDate = startDate || -Infinity;
23072 if (this.startDate !== -Infinity) {
23073 this.startDate = this.parseDate(this.startDate);
23076 this.updateNavArrows();
23079 setEndDate: function(endDate)
23081 this.endDate = endDate || Infinity;
23082 if (this.endDate !== Infinity) {
23083 this.endDate = this.parseDate(this.endDate);
23086 this.updateNavArrows();
23089 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23091 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23092 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23093 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23095 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23096 return parseInt(d, 10);
23099 this.updateNavArrows();
23102 updateNavArrows: function()
23104 if(this.singleMode){
23108 var d = new Date(this.viewDate),
23109 year = d.getUTCFullYear(),
23110 month = d.getUTCMonth();
23112 Roo.each(this.picker().select('.prev', true).elements, function(v){
23114 switch (this.viewMode) {
23117 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23123 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23130 Roo.each(this.picker().select('.next', true).elements, function(v){
23132 switch (this.viewMode) {
23135 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23141 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23149 moveMonth: function(date, dir)
23154 var new_date = new Date(date.valueOf()),
23155 day = new_date.getUTCDate(),
23156 month = new_date.getUTCMonth(),
23157 mag = Math.abs(dir),
23159 dir = dir > 0 ? 1 : -1;
23162 // If going back one month, make sure month is not current month
23163 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23165 return new_date.getUTCMonth() == month;
23167 // If going forward one month, make sure month is as expected
23168 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23170 return new_date.getUTCMonth() != new_month;
23172 new_month = month + dir;
23173 new_date.setUTCMonth(new_month);
23174 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23175 if (new_month < 0 || new_month > 11) {
23176 new_month = (new_month + 12) % 12;
23179 // For magnitudes >1, move one month at a time...
23180 for (var i=0; i<mag; i++) {
23181 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23182 new_date = this.moveMonth(new_date, dir);
23184 // ...then reset the day, keeping it in the new month
23185 new_month = new_date.getUTCMonth();
23186 new_date.setUTCDate(day);
23188 return new_month != new_date.getUTCMonth();
23191 // Common date-resetting loop -- if date is beyond end of month, make it
23194 new_date.setUTCDate(--day);
23195 new_date.setUTCMonth(new_month);
23200 moveYear: function(date, dir)
23202 return this.moveMonth(date, dir*12);
23205 dateWithinRange: function(date)
23207 return date >= this.startDate && date <= this.endDate;
23213 this.picker().remove();
23216 validateValue : function(value)
23218 if(this.getVisibilityEl().hasClass('hidden')){
23222 if(value.length < 1) {
23223 if(this.allowBlank){
23229 if(value.length < this.minLength){
23232 if(value.length > this.maxLength){
23236 var vt = Roo.form.VTypes;
23237 if(!vt[this.vtype](value, this)){
23241 if(typeof this.validator == "function"){
23242 var msg = this.validator(value);
23248 if(this.regex && !this.regex.test(value)){
23252 if(typeof(this.parseDate(value)) == 'undefined'){
23256 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23260 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23270 this.date = this.viewDate = '';
23272 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23277 Roo.apply(Roo.bootstrap.DateField, {
23288 html: '<i class="fa fa-arrow-left"/>'
23298 html: '<i class="fa fa-arrow-right"/>'
23340 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23341 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23342 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23343 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23344 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23357 navFnc: 'FullYear',
23362 navFnc: 'FullYear',
23367 Roo.apply(Roo.bootstrap.DateField, {
23371 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23375 cls: 'datepicker-days',
23379 cls: 'table-condensed',
23381 Roo.bootstrap.DateField.head,
23385 Roo.bootstrap.DateField.footer
23392 cls: 'datepicker-months',
23396 cls: 'table-condensed',
23398 Roo.bootstrap.DateField.head,
23399 Roo.bootstrap.DateField.content,
23400 Roo.bootstrap.DateField.footer
23407 cls: 'datepicker-years',
23411 cls: 'table-condensed',
23413 Roo.bootstrap.DateField.head,
23414 Roo.bootstrap.DateField.content,
23415 Roo.bootstrap.DateField.footer
23434 * @class Roo.bootstrap.TimeField
23435 * @extends Roo.bootstrap.Input
23436 * Bootstrap DateField class
23440 * Create a new TimeField
23441 * @param {Object} config The config object
23444 Roo.bootstrap.TimeField = function(config){
23445 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23449 * Fires when this field show.
23450 * @param {Roo.bootstrap.DateField} thisthis
23451 * @param {Mixed} date The date value
23456 * Fires when this field hide.
23457 * @param {Roo.bootstrap.DateField} this
23458 * @param {Mixed} date The date value
23463 * Fires when select a date.
23464 * @param {Roo.bootstrap.DateField} this
23465 * @param {Mixed} date The date value
23471 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23474 * @cfg {String} format
23475 * The default time format string which can be overriden for localization support. The format must be
23476 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23480 getAutoCreate : function()
23482 this.after = '<i class="fa far fa-clock"></i>';
23483 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23487 onRender: function(ct, position)
23490 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23492 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23494 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23496 this.pop = this.picker().select('>.datepicker-time',true).first();
23497 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23499 this.picker().on('mousedown', this.onMousedown, this);
23500 this.picker().on('click', this.onClick, this);
23502 this.picker().addClass('datepicker-dropdown');
23507 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23508 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23509 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23510 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23511 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23512 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23516 fireKey: function(e){
23517 if (!this.picker().isVisible()){
23518 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23524 e.preventDefault();
23532 this.onTogglePeriod();
23535 this.onIncrementMinutes();
23538 this.onDecrementMinutes();
23547 onClick: function(e) {
23548 e.stopPropagation();
23549 e.preventDefault();
23552 picker : function()
23554 return this.pickerEl;
23557 fillTime: function()
23559 var time = this.pop.select('tbody', true).first();
23561 time.dom.innerHTML = '';
23576 cls: 'hours-up fa fas fa-chevron-up'
23596 cls: 'minutes-up fa fas fa-chevron-up'
23617 cls: 'timepicker-hour',
23632 cls: 'timepicker-minute',
23647 cls: 'btn btn-primary period',
23669 cls: 'hours-down fa fas fa-chevron-down'
23689 cls: 'minutes-down fa fas fa-chevron-down'
23707 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23714 var hours = this.time.getHours();
23715 var minutes = this.time.getMinutes();
23728 hours = hours - 12;
23732 hours = '0' + hours;
23736 minutes = '0' + minutes;
23739 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23740 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23741 this.pop.select('button', true).first().dom.innerHTML = period;
23747 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23749 var cls = ['bottom'];
23751 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23758 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23762 //this.picker().setXY(20000,20000);
23763 this.picker().addClass(cls.join('-'));
23767 Roo.each(cls, function(c){
23772 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23773 //_this.picker().setTop(_this.inputEl().getHeight());
23777 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23779 //_this.picker().setTop(0 - _this.picker().getHeight());
23784 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23788 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23796 onFocus : function()
23798 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23802 onBlur : function()
23804 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23810 this.picker().show();
23815 this.fireEvent('show', this, this.date);
23820 this.picker().hide();
23823 this.fireEvent('hide', this, this.date);
23826 setTime : function()
23829 this.setValue(this.time.format(this.format));
23831 this.fireEvent('select', this, this.date);
23836 onMousedown: function(e){
23837 e.stopPropagation();
23838 e.preventDefault();
23841 onIncrementHours: function()
23843 Roo.log('onIncrementHours');
23844 this.time = this.time.add(Date.HOUR, 1);
23849 onDecrementHours: function()
23851 Roo.log('onDecrementHours');
23852 this.time = this.time.add(Date.HOUR, -1);
23856 onIncrementMinutes: function()
23858 Roo.log('onIncrementMinutes');
23859 this.time = this.time.add(Date.MINUTE, 1);
23863 onDecrementMinutes: function()
23865 Roo.log('onDecrementMinutes');
23866 this.time = this.time.add(Date.MINUTE, -1);
23870 onTogglePeriod: function()
23872 Roo.log('onTogglePeriod');
23873 this.time = this.time.add(Date.HOUR, 12);
23881 Roo.apply(Roo.bootstrap.TimeField, {
23885 cls: 'datepicker dropdown-menu',
23889 cls: 'datepicker-time',
23893 cls: 'table-condensed',
23922 cls: 'btn btn-info ok',
23950 * @class Roo.bootstrap.MonthField
23951 * @extends Roo.bootstrap.Input
23952 * Bootstrap MonthField class
23954 * @cfg {String} language default en
23957 * Create a new MonthField
23958 * @param {Object} config The config object
23961 Roo.bootstrap.MonthField = function(config){
23962 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23967 * Fires when this field show.
23968 * @param {Roo.bootstrap.MonthField} this
23969 * @param {Mixed} date The date value
23974 * Fires when this field hide.
23975 * @param {Roo.bootstrap.MonthField} this
23976 * @param {Mixed} date The date value
23981 * Fires when select a date.
23982 * @param {Roo.bootstrap.MonthField} this
23983 * @param {String} oldvalue The old value
23984 * @param {String} newvalue The new value
23990 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23992 onRender: function(ct, position)
23995 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23997 this.language = this.language || 'en';
23998 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23999 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24001 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24002 this.isInline = false;
24003 this.isInput = true;
24004 this.component = this.el.select('.add-on', true).first() || false;
24005 this.component = (this.component && this.component.length === 0) ? false : this.component;
24006 this.hasInput = this.component && this.inputEL().length;
24008 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24010 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24012 this.picker().on('mousedown', this.onMousedown, this);
24013 this.picker().on('click', this.onClick, this);
24015 this.picker().addClass('datepicker-dropdown');
24017 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24018 v.setStyle('width', '189px');
24025 if(this.isInline) {
24031 setValue: function(v, suppressEvent)
24033 var o = this.getValue();
24035 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24039 if(suppressEvent !== true){
24040 this.fireEvent('select', this, o, v);
24045 getValue: function()
24050 onClick: function(e)
24052 e.stopPropagation();
24053 e.preventDefault();
24055 var target = e.getTarget();
24057 if(target.nodeName.toLowerCase() === 'i'){
24058 target = Roo.get(target).dom.parentNode;
24061 var nodeName = target.nodeName;
24062 var className = target.className;
24063 var html = target.innerHTML;
24065 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24069 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24071 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24077 picker : function()
24079 return this.pickerEl;
24082 fillMonths: function()
24085 var months = this.picker().select('>.datepicker-months td', true).first();
24087 months.dom.innerHTML = '';
24093 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24096 months.createChild(month);
24105 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24106 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24109 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24110 e.removeClass('active');
24112 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24113 e.addClass('active');
24120 if(this.isInline) {
24124 this.picker().removeClass(['bottom', 'top']);
24126 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24128 * place to the top of element!
24132 this.picker().addClass('top');
24133 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24138 this.picker().addClass('bottom');
24140 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24143 onFocus : function()
24145 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24149 onBlur : function()
24151 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24153 var d = this.inputEl().getValue();
24162 this.picker().show();
24163 this.picker().select('>.datepicker-months', true).first().show();
24167 this.fireEvent('show', this, this.date);
24172 if(this.isInline) {
24175 this.picker().hide();
24176 this.fireEvent('hide', this, this.date);
24180 onMousedown: function(e)
24182 e.stopPropagation();
24183 e.preventDefault();
24188 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24192 fireKey: function(e)
24194 if (!this.picker().isVisible()){
24195 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24206 e.preventDefault();
24210 dir = e.keyCode == 37 ? -1 : 1;
24212 this.vIndex = this.vIndex + dir;
24214 if(this.vIndex < 0){
24218 if(this.vIndex > 11){
24222 if(isNaN(this.vIndex)){
24226 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24232 dir = e.keyCode == 38 ? -1 : 1;
24234 this.vIndex = this.vIndex + dir * 4;
24236 if(this.vIndex < 0){
24240 if(this.vIndex > 11){
24244 if(isNaN(this.vIndex)){
24248 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24253 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24254 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24258 e.preventDefault();
24261 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24262 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24278 this.picker().remove();
24283 Roo.apply(Roo.bootstrap.MonthField, {
24302 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24303 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24308 Roo.apply(Roo.bootstrap.MonthField, {
24312 cls: 'datepicker dropdown-menu roo-dynamic',
24316 cls: 'datepicker-months',
24320 cls: 'table-condensed',
24322 Roo.bootstrap.DateField.content
24342 * @class Roo.bootstrap.CheckBox
24343 * @extends Roo.bootstrap.Input
24344 * Bootstrap CheckBox class
24346 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24347 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24348 * @cfg {String} boxLabel The text that appears beside the checkbox
24349 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24350 * @cfg {Boolean} checked initnal the element
24351 * @cfg {Boolean} inline inline the element (default false)
24352 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24353 * @cfg {String} tooltip label tooltip
24356 * Create a new CheckBox
24357 * @param {Object} config The config object
24360 Roo.bootstrap.CheckBox = function(config){
24361 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24366 * Fires when the element is checked or unchecked.
24367 * @param {Roo.bootstrap.CheckBox} this This input
24368 * @param {Boolean} checked The new checked value
24373 * Fires when the element is click.
24374 * @param {Roo.bootstrap.CheckBox} this This input
24381 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24383 inputType: 'checkbox',
24392 // checkbox success does not make any sense really..
24397 getAutoCreate : function()
24399 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24405 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24408 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24414 type : this.inputType,
24415 value : this.inputValue,
24416 cls : 'roo-' + this.inputType, //'form-box',
24417 placeholder : this.placeholder || ''
24421 if(this.inputType != 'radio'){
24425 cls : 'roo-hidden-value',
24426 value : this.checked ? this.inputValue : this.valueOff
24431 if (this.weight) { // Validity check?
24432 cfg.cls += " " + this.inputType + "-" + this.weight;
24435 if (this.disabled) {
24436 input.disabled=true;
24440 input.checked = this.checked;
24445 input.name = this.name;
24447 if(this.inputType != 'radio'){
24448 hidden.name = this.name;
24449 input.name = '_hidden_' + this.name;
24454 input.cls += ' input-' + this.size;
24459 ['xs','sm','md','lg'].map(function(size){
24460 if (settings[size]) {
24461 cfg.cls += ' col-' + size + '-' + settings[size];
24465 var inputblock = input;
24467 if (this.before || this.after) {
24470 cls : 'input-group',
24475 inputblock.cn.push({
24477 cls : 'input-group-addon',
24482 inputblock.cn.push(input);
24484 if(this.inputType != 'radio'){
24485 inputblock.cn.push(hidden);
24489 inputblock.cn.push({
24491 cls : 'input-group-addon',
24497 var boxLabelCfg = false;
24503 //'for': id, // box label is handled by onclick - so no for...
24505 html: this.boxLabel
24508 boxLabelCfg.tooltip = this.tooltip;
24514 if (align ==='left' && this.fieldLabel.length) {
24515 // Roo.log("left and has label");
24520 cls : 'control-label',
24521 html : this.fieldLabel
24532 cfg.cn[1].cn.push(boxLabelCfg);
24535 if(this.labelWidth > 12){
24536 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24539 if(this.labelWidth < 13 && this.labelmd == 0){
24540 this.labelmd = this.labelWidth;
24543 if(this.labellg > 0){
24544 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24545 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24548 if(this.labelmd > 0){
24549 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24550 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24553 if(this.labelsm > 0){
24554 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24555 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24558 if(this.labelxs > 0){
24559 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24560 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24563 } else if ( this.fieldLabel.length) {
24564 // Roo.log(" label");
24568 tag: this.boxLabel ? 'span' : 'label',
24570 cls: 'control-label box-input-label',
24571 //cls : 'input-group-addon',
24572 html : this.fieldLabel
24579 cfg.cn.push(boxLabelCfg);
24584 // Roo.log(" no label && no align");
24585 cfg.cn = [ inputblock ] ;
24587 cfg.cn.push(boxLabelCfg);
24595 if(this.inputType != 'radio'){
24596 cfg.cn.push(hidden);
24604 * return the real input element.
24606 inputEl: function ()
24608 return this.el.select('input.roo-' + this.inputType,true).first();
24610 hiddenEl: function ()
24612 return this.el.select('input.roo-hidden-value',true).first();
24615 labelEl: function()
24617 return this.el.select('label.control-label',true).first();
24619 /* depricated... */
24623 return this.labelEl();
24626 boxLabelEl: function()
24628 return this.el.select('label.box-label',true).first();
24631 initEvents : function()
24633 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24635 this.inputEl().on('click', this.onClick, this);
24637 if (this.boxLabel) {
24638 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24641 this.startValue = this.getValue();
24644 Roo.bootstrap.CheckBox.register(this);
24648 onClick : function(e)
24650 if(this.fireEvent('click', this, e) !== false){
24651 this.setChecked(!this.checked);
24656 setChecked : function(state,suppressEvent)
24658 this.startValue = this.getValue();
24660 if(this.inputType == 'radio'){
24662 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24663 e.dom.checked = false;
24666 this.inputEl().dom.checked = true;
24668 this.inputEl().dom.value = this.inputValue;
24670 if(suppressEvent !== true){
24671 this.fireEvent('check', this, true);
24679 this.checked = state;
24681 this.inputEl().dom.checked = state;
24684 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24686 if(suppressEvent !== true){
24687 this.fireEvent('check', this, state);
24693 getValue : function()
24695 if(this.inputType == 'radio'){
24696 return this.getGroupValue();
24699 return this.hiddenEl().dom.value;
24703 getGroupValue : function()
24705 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24709 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24712 setValue : function(v,suppressEvent)
24714 if(this.inputType == 'radio'){
24715 this.setGroupValue(v, suppressEvent);
24719 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24724 setGroupValue : function(v, suppressEvent)
24726 this.startValue = this.getValue();
24728 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24729 e.dom.checked = false;
24731 if(e.dom.value == v){
24732 e.dom.checked = true;
24736 if(suppressEvent !== true){
24737 this.fireEvent('check', this, true);
24745 validate : function()
24747 if(this.getVisibilityEl().hasClass('hidden')){
24753 (this.inputType == 'radio' && this.validateRadio()) ||
24754 (this.inputType == 'checkbox' && this.validateCheckbox())
24760 this.markInvalid();
24764 validateRadio : function()
24766 if(this.getVisibilityEl().hasClass('hidden')){
24770 if(this.allowBlank){
24776 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24777 if(!e.dom.checked){
24789 validateCheckbox : function()
24792 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24793 //return (this.getValue() == this.inputValue) ? true : false;
24796 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24804 for(var i in group){
24805 if(group[i].el.isVisible(true)){
24813 for(var i in group){
24818 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24825 * Mark this field as valid
24827 markValid : function()
24831 this.fireEvent('valid', this);
24833 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24836 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24843 if(this.inputType == 'radio'){
24844 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24845 var fg = e.findParent('.form-group', false, true);
24846 if (Roo.bootstrap.version == 3) {
24847 fg.removeClass([_this.invalidClass, _this.validClass]);
24848 fg.addClass(_this.validClass);
24850 fg.removeClass(['is-valid', 'is-invalid']);
24851 fg.addClass('is-valid');
24859 var fg = this.el.findParent('.form-group', false, true);
24860 if (Roo.bootstrap.version == 3) {
24861 fg.removeClass([this.invalidClass, this.validClass]);
24862 fg.addClass(this.validClass);
24864 fg.removeClass(['is-valid', 'is-invalid']);
24865 fg.addClass('is-valid');
24870 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24876 for(var i in group){
24877 var fg = group[i].el.findParent('.form-group', false, true);
24878 if (Roo.bootstrap.version == 3) {
24879 fg.removeClass([this.invalidClass, this.validClass]);
24880 fg.addClass(this.validClass);
24882 fg.removeClass(['is-valid', 'is-invalid']);
24883 fg.addClass('is-valid');
24889 * Mark this field as invalid
24890 * @param {String} msg The validation message
24892 markInvalid : function(msg)
24894 if(this.allowBlank){
24900 this.fireEvent('invalid', this, msg);
24902 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24905 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24909 label.markInvalid();
24912 if(this.inputType == 'radio'){
24914 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24915 var fg = e.findParent('.form-group', false, true);
24916 if (Roo.bootstrap.version == 3) {
24917 fg.removeClass([_this.invalidClass, _this.validClass]);
24918 fg.addClass(_this.invalidClass);
24920 fg.removeClass(['is-invalid', 'is-valid']);
24921 fg.addClass('is-invalid');
24929 var fg = this.el.findParent('.form-group', false, true);
24930 if (Roo.bootstrap.version == 3) {
24931 fg.removeClass([_this.invalidClass, _this.validClass]);
24932 fg.addClass(_this.invalidClass);
24934 fg.removeClass(['is-invalid', 'is-valid']);
24935 fg.addClass('is-invalid');
24940 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24946 for(var i in group){
24947 var fg = group[i].el.findParent('.form-group', false, true);
24948 if (Roo.bootstrap.version == 3) {
24949 fg.removeClass([_this.invalidClass, _this.validClass]);
24950 fg.addClass(_this.invalidClass);
24952 fg.removeClass(['is-invalid', 'is-valid']);
24953 fg.addClass('is-invalid');
24959 clearInvalid : function()
24961 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24963 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24965 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24967 if (label && label.iconEl) {
24968 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24969 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24973 disable : function()
24975 if(this.inputType != 'radio'){
24976 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24983 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24984 _this.getActionEl().addClass(this.disabledClass);
24985 e.dom.disabled = true;
24989 this.disabled = true;
24990 this.fireEvent("disable", this);
24994 enable : function()
24996 if(this.inputType != 'radio'){
24997 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25004 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25005 _this.getActionEl().removeClass(this.disabledClass);
25006 e.dom.disabled = false;
25010 this.disabled = false;
25011 this.fireEvent("enable", this);
25015 setBoxLabel : function(v)
25020 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25026 Roo.apply(Roo.bootstrap.CheckBox, {
25031 * register a CheckBox Group
25032 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25034 register : function(checkbox)
25036 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25037 this.groups[checkbox.groupId] = {};
25040 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25044 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25048 * fetch a CheckBox Group based on the group ID
25049 * @param {string} the group ID
25050 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25052 get: function(groupId) {
25053 if (typeof(this.groups[groupId]) == 'undefined') {
25057 return this.groups[groupId] ;
25070 * @class Roo.bootstrap.Radio
25071 * @extends Roo.bootstrap.Component
25072 * Bootstrap Radio class
25073 * @cfg {String} boxLabel - the label associated
25074 * @cfg {String} value - the value of radio
25077 * Create a new Radio
25078 * @param {Object} config The config object
25080 Roo.bootstrap.Radio = function(config){
25081 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25085 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25091 getAutoCreate : function()
25095 cls : 'form-group radio',
25100 html : this.boxLabel
25108 initEvents : function()
25110 this.parent().register(this);
25112 this.el.on('click', this.onClick, this);
25116 onClick : function(e)
25118 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25119 this.setChecked(true);
25123 setChecked : function(state, suppressEvent)
25125 this.parent().setValue(this.value, suppressEvent);
25129 setBoxLabel : function(v)
25134 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25149 * @class Roo.bootstrap.SecurePass
25150 * @extends Roo.bootstrap.Input
25151 * Bootstrap SecurePass class
25155 * Create a new SecurePass
25156 * @param {Object} config The config object
25159 Roo.bootstrap.SecurePass = function (config) {
25160 // these go here, so the translation tool can replace them..
25162 PwdEmpty: "Please type a password, and then retype it to confirm.",
25163 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25164 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25165 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25166 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25167 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25168 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25169 TooWeak: "Your password is Too Weak."
25171 this.meterLabel = "Password strength:";
25172 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25173 this.meterClass = [
25174 "roo-password-meter-tooweak",
25175 "roo-password-meter-weak",
25176 "roo-password-meter-medium",
25177 "roo-password-meter-strong",
25178 "roo-password-meter-grey"
25183 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25186 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25188 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25190 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25191 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25192 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25193 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25194 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25195 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25196 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25206 * @cfg {String/Object} Label for the strength meter (defaults to
25207 * 'Password strength:')
25212 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25213 * ['Weak', 'Medium', 'Strong'])
25216 pwdStrengths: false,
25229 initEvents: function ()
25231 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25233 if (this.el.is('input[type=password]') && Roo.isSafari) {
25234 this.el.on('keydown', this.SafariOnKeyDown, this);
25237 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25240 onRender: function (ct, position)
25242 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25243 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25244 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25246 this.trigger.createChild({
25251 cls: 'roo-password-meter-grey col-xs-12',
25254 //width: this.meterWidth + 'px'
25258 cls: 'roo-password-meter-text'
25264 if (this.hideTrigger) {
25265 this.trigger.setDisplayed(false);
25267 this.setSize(this.width || '', this.height || '');
25270 onDestroy: function ()
25272 if (this.trigger) {
25273 this.trigger.removeAllListeners();
25274 this.trigger.remove();
25277 this.wrap.remove();
25279 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25282 checkStrength: function ()
25284 var pwd = this.inputEl().getValue();
25285 if (pwd == this._lastPwd) {
25290 if (this.ClientSideStrongPassword(pwd)) {
25292 } else if (this.ClientSideMediumPassword(pwd)) {
25294 } else if (this.ClientSideWeakPassword(pwd)) {
25300 Roo.log('strength1: ' + strength);
25302 //var pm = this.trigger.child('div/div/div').dom;
25303 var pm = this.trigger.child('div/div');
25304 pm.removeClass(this.meterClass);
25305 pm.addClass(this.meterClass[strength]);
25308 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25310 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25312 this._lastPwd = pwd;
25316 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25318 this._lastPwd = '';
25320 var pm = this.trigger.child('div/div');
25321 pm.removeClass(this.meterClass);
25322 pm.addClass('roo-password-meter-grey');
25325 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25328 this.inputEl().dom.type='password';
25331 validateValue: function (value)
25333 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25336 if (value.length == 0) {
25337 if (this.allowBlank) {
25338 this.clearInvalid();
25342 this.markInvalid(this.errors.PwdEmpty);
25343 this.errorMsg = this.errors.PwdEmpty;
25351 if (!value.match(/[\x21-\x7e]+/)) {
25352 this.markInvalid(this.errors.PwdBadChar);
25353 this.errorMsg = this.errors.PwdBadChar;
25356 if (value.length < 6) {
25357 this.markInvalid(this.errors.PwdShort);
25358 this.errorMsg = this.errors.PwdShort;
25361 if (value.length > 16) {
25362 this.markInvalid(this.errors.PwdLong);
25363 this.errorMsg = this.errors.PwdLong;
25367 if (this.ClientSideStrongPassword(value)) {
25369 } else if (this.ClientSideMediumPassword(value)) {
25371 } else if (this.ClientSideWeakPassword(value)) {
25378 if (strength < 2) {
25379 //this.markInvalid(this.errors.TooWeak);
25380 this.errorMsg = this.errors.TooWeak;
25385 console.log('strength2: ' + strength);
25387 //var pm = this.trigger.child('div/div/div').dom;
25389 var pm = this.trigger.child('div/div');
25390 pm.removeClass(this.meterClass);
25391 pm.addClass(this.meterClass[strength]);
25393 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25395 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25397 this.errorMsg = '';
25401 CharacterSetChecks: function (type)
25404 this.fResult = false;
25407 isctype: function (character, type)
25410 case this.kCapitalLetter:
25411 if (character >= 'A' && character <= 'Z') {
25416 case this.kSmallLetter:
25417 if (character >= 'a' && character <= 'z') {
25423 if (character >= '0' && character <= '9') {
25428 case this.kPunctuation:
25429 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25440 IsLongEnough: function (pwd, size)
25442 return !(pwd == null || isNaN(size) || pwd.length < size);
25445 SpansEnoughCharacterSets: function (word, nb)
25447 if (!this.IsLongEnough(word, nb))
25452 var characterSetChecks = new Array(
25453 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25454 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25457 for (var index = 0; index < word.length; ++index) {
25458 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25459 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25460 characterSetChecks[nCharSet].fResult = true;
25467 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25468 if (characterSetChecks[nCharSet].fResult) {
25473 if (nCharSets < nb) {
25479 ClientSideStrongPassword: function (pwd)
25481 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25484 ClientSideMediumPassword: function (pwd)
25486 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25489 ClientSideWeakPassword: function (pwd)
25491 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25494 })//<script type="text/javascript">
25497 * Based Ext JS Library 1.1.1
25498 * Copyright(c) 2006-2007, Ext JS, LLC.
25504 * @class Roo.HtmlEditorCore
25505 * @extends Roo.Component
25506 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25508 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25511 Roo.HtmlEditorCore = function(config){
25514 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25519 * @event initialize
25520 * Fires when the editor is fully initialized (including the iframe)
25521 * @param {Roo.HtmlEditorCore} this
25526 * Fires when the editor is first receives the focus. Any insertion must wait
25527 * until after this event.
25528 * @param {Roo.HtmlEditorCore} this
25532 * @event beforesync
25533 * Fires before the textarea is updated with content from the editor iframe. Return false
25534 * to cancel the sync.
25535 * @param {Roo.HtmlEditorCore} this
25536 * @param {String} html
25540 * @event beforepush
25541 * Fires before the iframe editor is updated with content from the textarea. Return false
25542 * to cancel the push.
25543 * @param {Roo.HtmlEditorCore} this
25544 * @param {String} html
25549 * Fires when the textarea is updated with content from the editor iframe.
25550 * @param {Roo.HtmlEditorCore} this
25551 * @param {String} html
25556 * Fires when the iframe editor is updated with content from the textarea.
25557 * @param {Roo.HtmlEditorCore} this
25558 * @param {String} html
25563 * @event editorevent
25564 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25565 * @param {Roo.HtmlEditorCore} this
25571 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25573 // defaults : white / black...
25574 this.applyBlacklists();
25581 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25585 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25591 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25596 * @cfg {Number} height (in pixels)
25600 * @cfg {Number} width (in pixels)
25605 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25608 stylesheets: false,
25613 // private properties
25614 validationEvent : false,
25616 initialized : false,
25618 sourceEditMode : false,
25619 onFocus : Roo.emptyFn,
25621 hideMode:'offsets',
25625 // blacklist + whitelisted elements..
25632 * Protected method that will not generally be called directly. It
25633 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25634 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25636 getDocMarkup : function(){
25640 // inherit styels from page...??
25641 if (this.stylesheets === false) {
25643 Roo.get(document.head).select('style').each(function(node) {
25644 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25647 Roo.get(document.head).select('link').each(function(node) {
25648 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25651 } else if (!this.stylesheets.length) {
25653 st = '<style type="text/css">' +
25654 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25657 for (var i in this.stylesheets) {
25658 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25663 st += '<style type="text/css">' +
25664 'IMG { cursor: pointer } ' +
25667 var cls = 'roo-htmleditor-body';
25669 if(this.bodyCls.length){
25670 cls += ' ' + this.bodyCls;
25673 return '<html><head>' + st +
25674 //<style type="text/css">' +
25675 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25677 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25681 onRender : function(ct, position)
25684 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25685 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25688 this.el.dom.style.border = '0 none';
25689 this.el.dom.setAttribute('tabIndex', -1);
25690 this.el.addClass('x-hidden hide');
25694 if(Roo.isIE){ // fix IE 1px bogus margin
25695 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25699 this.frameId = Roo.id();
25703 var iframe = this.owner.wrap.createChild({
25705 cls: 'form-control', // bootstrap..
25707 name: this.frameId,
25708 frameBorder : 'no',
25709 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25714 this.iframe = iframe.dom;
25716 this.assignDocWin();
25718 this.doc.designMode = 'on';
25721 this.doc.write(this.getDocMarkup());
25725 var task = { // must defer to wait for browser to be ready
25727 //console.log("run task?" + this.doc.readyState);
25728 this.assignDocWin();
25729 if(this.doc.body || this.doc.readyState == 'complete'){
25731 this.doc.designMode="on";
25735 Roo.TaskMgr.stop(task);
25736 this.initEditor.defer(10, this);
25743 Roo.TaskMgr.start(task);
25748 onResize : function(w, h)
25750 Roo.log('resize: ' +w + ',' + h );
25751 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25755 if(typeof w == 'number'){
25757 this.iframe.style.width = w + 'px';
25759 if(typeof h == 'number'){
25761 this.iframe.style.height = h + 'px';
25763 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25770 * Toggles the editor between standard and source edit mode.
25771 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25773 toggleSourceEdit : function(sourceEditMode){
25775 this.sourceEditMode = sourceEditMode === true;
25777 if(this.sourceEditMode){
25779 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25782 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25783 //this.iframe.className = '';
25786 //this.setSize(this.owner.wrap.getSize());
25787 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25794 * Protected method that will not generally be called directly. If you need/want
25795 * custom HTML cleanup, this is the method you should override.
25796 * @param {String} html The HTML to be cleaned
25797 * return {String} The cleaned HTML
25799 cleanHtml : function(html){
25800 html = String(html);
25801 if(html.length > 5){
25802 if(Roo.isSafari){ // strip safari nonsense
25803 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25806 if(html == ' '){
25813 * HTML Editor -> Textarea
25814 * Protected method that will not generally be called directly. Syncs the contents
25815 * of the editor iframe with the textarea.
25817 syncValue : function(){
25818 if(this.initialized){
25819 var bd = (this.doc.body || this.doc.documentElement);
25820 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25821 var html = bd.innerHTML;
25823 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25824 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25826 html = '<div style="'+m[0]+'">' + html + '</div>';
25829 html = this.cleanHtml(html);
25830 // fix up the special chars.. normaly like back quotes in word...
25831 // however we do not want to do this with chinese..
25832 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25834 var cc = match.charCodeAt();
25836 // Get the character value, handling surrogate pairs
25837 if (match.length == 2) {
25838 // It's a surrogate pair, calculate the Unicode code point
25839 var high = match.charCodeAt(0) - 0xD800;
25840 var low = match.charCodeAt(1) - 0xDC00;
25841 cc = (high * 0x400) + low + 0x10000;
25843 (cc >= 0x4E00 && cc < 0xA000 ) ||
25844 (cc >= 0x3400 && cc < 0x4E00 ) ||
25845 (cc >= 0xf900 && cc < 0xfb00 )
25850 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25851 return "&#" + cc + ";";
25858 if(this.owner.fireEvent('beforesync', this, html) !== false){
25859 this.el.dom.value = html;
25860 this.owner.fireEvent('sync', this, html);
25866 * Protected method that will not generally be called directly. Pushes the value of the textarea
25867 * into the iframe editor.
25869 pushValue : function(){
25870 if(this.initialized){
25871 var v = this.el.dom.value.trim();
25873 // if(v.length < 1){
25877 if(this.owner.fireEvent('beforepush', this, v) !== false){
25878 var d = (this.doc.body || this.doc.documentElement);
25880 this.cleanUpPaste();
25881 this.el.dom.value = d.innerHTML;
25882 this.owner.fireEvent('push', this, v);
25888 deferFocus : function(){
25889 this.focus.defer(10, this);
25893 focus : function(){
25894 if(this.win && !this.sourceEditMode){
25901 assignDocWin: function()
25903 var iframe = this.iframe;
25906 this.doc = iframe.contentWindow.document;
25907 this.win = iframe.contentWindow;
25909 // if (!Roo.get(this.frameId)) {
25912 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25913 // this.win = Roo.get(this.frameId).dom.contentWindow;
25915 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25919 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25920 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25925 initEditor : function(){
25926 //console.log("INIT EDITOR");
25927 this.assignDocWin();
25931 this.doc.designMode="on";
25933 this.doc.write(this.getDocMarkup());
25936 var dbody = (this.doc.body || this.doc.documentElement);
25937 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25938 // this copies styles from the containing element into thsi one..
25939 // not sure why we need all of this..
25940 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25942 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25943 //ss['background-attachment'] = 'fixed'; // w3c
25944 dbody.bgProperties = 'fixed'; // ie
25945 //Roo.DomHelper.applyStyles(dbody, ss);
25946 Roo.EventManager.on(this.doc, {
25947 //'mousedown': this.onEditorEvent,
25948 'mouseup': this.onEditorEvent,
25949 'dblclick': this.onEditorEvent,
25950 'click': this.onEditorEvent,
25951 'keyup': this.onEditorEvent,
25956 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25958 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25959 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25961 this.initialized = true;
25963 this.owner.fireEvent('initialize', this);
25968 onDestroy : function(){
25974 //for (var i =0; i < this.toolbars.length;i++) {
25975 // // fixme - ask toolbars for heights?
25976 // this.toolbars[i].onDestroy();
25979 //this.wrap.dom.innerHTML = '';
25980 //this.wrap.remove();
25985 onFirstFocus : function(){
25987 this.assignDocWin();
25990 this.activated = true;
25993 if(Roo.isGecko){ // prevent silly gecko errors
25995 var s = this.win.getSelection();
25996 if(!s.focusNode || s.focusNode.nodeType != 3){
25997 var r = s.getRangeAt(0);
25998 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26003 this.execCmd('useCSS', true);
26004 this.execCmd('styleWithCSS', false);
26007 this.owner.fireEvent('activate', this);
26011 adjustFont: function(btn){
26012 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26013 //if(Roo.isSafari){ // safari
26016 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26017 if(Roo.isSafari){ // safari
26018 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26019 v = (v < 10) ? 10 : v;
26020 v = (v > 48) ? 48 : v;
26021 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26026 v = Math.max(1, v+adjust);
26028 this.execCmd('FontSize', v );
26031 onEditorEvent : function(e)
26033 this.owner.fireEvent('editorevent', this, e);
26034 // this.updateToolbar();
26035 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26038 insertTag : function(tg)
26040 // could be a bit smarter... -> wrap the current selected tRoo..
26041 if (tg.toLowerCase() == 'span' ||
26042 tg.toLowerCase() == 'code' ||
26043 tg.toLowerCase() == 'sup' ||
26044 tg.toLowerCase() == 'sub'
26047 range = this.createRange(this.getSelection());
26048 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26049 wrappingNode.appendChild(range.extractContents());
26050 range.insertNode(wrappingNode);
26057 this.execCmd("formatblock", tg);
26061 insertText : function(txt)
26065 var range = this.createRange();
26066 range.deleteContents();
26067 //alert(Sender.getAttribute('label'));
26069 range.insertNode(this.doc.createTextNode(txt));
26075 * Executes a Midas editor command on the editor document and performs necessary focus and
26076 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26077 * @param {String} cmd The Midas command
26078 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26080 relayCmd : function(cmd, value){
26082 this.execCmd(cmd, value);
26083 this.owner.fireEvent('editorevent', this);
26084 //this.updateToolbar();
26085 this.owner.deferFocus();
26089 * Executes a Midas editor command directly on the editor document.
26090 * For visual commands, you should use {@link #relayCmd} instead.
26091 * <b>This should only be called after the editor is initialized.</b>
26092 * @param {String} cmd The Midas command
26093 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26095 execCmd : function(cmd, value){
26096 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26103 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26105 * @param {String} text | dom node..
26107 insertAtCursor : function(text)
26110 if(!this.activated){
26116 var r = this.doc.selection.createRange();
26127 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26131 // from jquery ui (MIT licenced)
26133 var win = this.win;
26135 if (win.getSelection && win.getSelection().getRangeAt) {
26136 range = win.getSelection().getRangeAt(0);
26137 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26138 range.insertNode(node);
26139 } else if (win.document.selection && win.document.selection.createRange) {
26140 // no firefox support
26141 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26142 win.document.selection.createRange().pasteHTML(txt);
26144 // no firefox support
26145 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26146 this.execCmd('InsertHTML', txt);
26155 mozKeyPress : function(e){
26157 var c = e.getCharCode(), cmd;
26160 c = String.fromCharCode(c).toLowerCase();
26174 this.cleanUpPaste.defer(100, this);
26182 e.preventDefault();
26190 fixKeys : function(){ // load time branching for fastest keydown performance
26192 return function(e){
26193 var k = e.getKey(), r;
26196 r = this.doc.selection.createRange();
26199 r.pasteHTML('    ');
26206 r = this.doc.selection.createRange();
26208 var target = r.parentElement();
26209 if(!target || target.tagName.toLowerCase() != 'li'){
26211 r.pasteHTML('<br />');
26217 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26218 this.cleanUpPaste.defer(100, this);
26224 }else if(Roo.isOpera){
26225 return function(e){
26226 var k = e.getKey();
26230 this.execCmd('InsertHTML','    ');
26233 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26234 this.cleanUpPaste.defer(100, this);
26239 }else if(Roo.isSafari){
26240 return function(e){
26241 var k = e.getKey();
26245 this.execCmd('InsertText','\t');
26249 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26250 this.cleanUpPaste.defer(100, this);
26258 getAllAncestors: function()
26260 var p = this.getSelectedNode();
26263 a.push(p); // push blank onto stack..
26264 p = this.getParentElement();
26268 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26272 a.push(this.doc.body);
26276 lastSelNode : false,
26279 getSelection : function()
26281 this.assignDocWin();
26282 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26285 getSelectedNode: function()
26287 // this may only work on Gecko!!!
26289 // should we cache this!!!!
26294 var range = this.createRange(this.getSelection()).cloneRange();
26297 var parent = range.parentElement();
26299 var testRange = range.duplicate();
26300 testRange.moveToElementText(parent);
26301 if (testRange.inRange(range)) {
26304 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26307 parent = parent.parentElement;
26312 // is ancestor a text element.
26313 var ac = range.commonAncestorContainer;
26314 if (ac.nodeType == 3) {
26315 ac = ac.parentNode;
26318 var ar = ac.childNodes;
26321 var other_nodes = [];
26322 var has_other_nodes = false;
26323 for (var i=0;i<ar.length;i++) {
26324 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26327 // fullly contained node.
26329 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26334 // probably selected..
26335 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26336 other_nodes.push(ar[i]);
26340 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26345 has_other_nodes = true;
26347 if (!nodes.length && other_nodes.length) {
26348 nodes= other_nodes;
26350 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26356 createRange: function(sel)
26358 // this has strange effects when using with
26359 // top toolbar - not sure if it's a great idea.
26360 //this.editor.contentWindow.focus();
26361 if (typeof sel != "undefined") {
26363 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26365 return this.doc.createRange();
26368 return this.doc.createRange();
26371 getParentElement: function()
26374 this.assignDocWin();
26375 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26377 var range = this.createRange(sel);
26380 var p = range.commonAncestorContainer;
26381 while (p.nodeType == 3) { // text node
26392 * Range intersection.. the hard stuff...
26396 * [ -- selected range --- ]
26400 * if end is before start or hits it. fail.
26401 * if start is after end or hits it fail.
26403 * if either hits (but other is outside. - then it's not
26409 // @see http://www.thismuchiknow.co.uk/?p=64.
26410 rangeIntersectsNode : function(range, node)
26412 var nodeRange = node.ownerDocument.createRange();
26414 nodeRange.selectNode(node);
26416 nodeRange.selectNodeContents(node);
26419 var rangeStartRange = range.cloneRange();
26420 rangeStartRange.collapse(true);
26422 var rangeEndRange = range.cloneRange();
26423 rangeEndRange.collapse(false);
26425 var nodeStartRange = nodeRange.cloneRange();
26426 nodeStartRange.collapse(true);
26428 var nodeEndRange = nodeRange.cloneRange();
26429 nodeEndRange.collapse(false);
26431 return rangeStartRange.compareBoundaryPoints(
26432 Range.START_TO_START, nodeEndRange) == -1 &&
26433 rangeEndRange.compareBoundaryPoints(
26434 Range.START_TO_START, nodeStartRange) == 1;
26438 rangeCompareNode : function(range, node)
26440 var nodeRange = node.ownerDocument.createRange();
26442 nodeRange.selectNode(node);
26444 nodeRange.selectNodeContents(node);
26448 range.collapse(true);
26450 nodeRange.collapse(true);
26452 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26453 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26455 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26457 var nodeIsBefore = ss == 1;
26458 var nodeIsAfter = ee == -1;
26460 if (nodeIsBefore && nodeIsAfter) {
26463 if (!nodeIsBefore && nodeIsAfter) {
26464 return 1; //right trailed.
26467 if (nodeIsBefore && !nodeIsAfter) {
26468 return 2; // left trailed.
26474 // private? - in a new class?
26475 cleanUpPaste : function()
26477 // cleans up the whole document..
26478 Roo.log('cleanuppaste');
26480 this.cleanUpChildren(this.doc.body);
26481 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26482 if (clean != this.doc.body.innerHTML) {
26483 this.doc.body.innerHTML = clean;
26488 cleanWordChars : function(input) {// change the chars to hex code
26489 var he = Roo.HtmlEditorCore;
26491 var output = input;
26492 Roo.each(he.swapCodes, function(sw) {
26493 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26495 output = output.replace(swapper, sw[1]);
26502 cleanUpChildren : function (n)
26504 if (!n.childNodes.length) {
26507 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26508 this.cleanUpChild(n.childNodes[i]);
26515 cleanUpChild : function (node)
26518 //console.log(node);
26519 if (node.nodeName == "#text") {
26520 // clean up silly Windows -- stuff?
26523 if (node.nodeName == "#comment") {
26524 node.parentNode.removeChild(node);
26525 // clean up silly Windows -- stuff?
26528 var lcname = node.tagName.toLowerCase();
26529 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26530 // whitelist of tags..
26532 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26534 node.parentNode.removeChild(node);
26539 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26541 // spans with no attributes - just remove them..
26542 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26543 remove_keep_children = true;
26546 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26547 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26549 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26550 // remove_keep_children = true;
26553 if (remove_keep_children) {
26554 this.cleanUpChildren(node);
26555 // inserts everything just before this node...
26556 while (node.childNodes.length) {
26557 var cn = node.childNodes[0];
26558 node.removeChild(cn);
26559 node.parentNode.insertBefore(cn, node);
26561 node.parentNode.removeChild(node);
26565 if (!node.attributes || !node.attributes.length) {
26570 this.cleanUpChildren(node);
26574 function cleanAttr(n,v)
26577 if (v.match(/^\./) || v.match(/^\//)) {
26580 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26583 if (v.match(/^#/)) {
26586 if (v.match(/^\{/)) { // allow template editing.
26589 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26590 node.removeAttribute(n);
26594 var cwhite = this.cwhite;
26595 var cblack = this.cblack;
26597 function cleanStyle(n,v)
26599 if (v.match(/expression/)) { //XSS?? should we even bother..
26600 node.removeAttribute(n);
26604 var parts = v.split(/;/);
26607 Roo.each(parts, function(p) {
26608 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26612 var l = p.split(':').shift().replace(/\s+/g,'');
26613 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26615 if ( cwhite.length && cblack.indexOf(l) > -1) {
26616 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26617 //node.removeAttribute(n);
26621 // only allow 'c whitelisted system attributes'
26622 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26623 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26624 //node.removeAttribute(n);
26634 if (clean.length) {
26635 node.setAttribute(n, clean.join(';'));
26637 node.removeAttribute(n);
26643 for (var i = node.attributes.length-1; i > -1 ; i--) {
26644 var a = node.attributes[i];
26647 if (a.name.toLowerCase().substr(0,2)=='on') {
26648 node.removeAttribute(a.name);
26651 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26652 node.removeAttribute(a.name);
26655 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26656 cleanAttr(a.name,a.value); // fixme..
26659 if (a.name == 'style') {
26660 cleanStyle(a.name,a.value);
26663 /// clean up MS crap..
26664 // tecnically this should be a list of valid class'es..
26667 if (a.name == 'class') {
26668 if (a.value.match(/^Mso/)) {
26669 node.removeAttribute('class');
26672 if (a.value.match(/^body$/)) {
26673 node.removeAttribute('class');
26684 this.cleanUpChildren(node);
26690 * Clean up MS wordisms...
26692 cleanWord : function(node)
26695 this.cleanWord(this.doc.body);
26700 node.nodeName == 'SPAN' &&
26701 !node.hasAttributes() &&
26702 node.childNodes.length == 1 &&
26703 node.firstChild.nodeName == "#text"
26705 var textNode = node.firstChild;
26706 node.removeChild(textNode);
26707 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26708 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26710 node.parentNode.insertBefore(textNode, node);
26711 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26712 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26714 node.parentNode.removeChild(node);
26717 if (node.nodeName == "#text") {
26718 // clean up silly Windows -- stuff?
26721 if (node.nodeName == "#comment") {
26722 node.parentNode.removeChild(node);
26723 // clean up silly Windows -- stuff?
26727 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26728 node.parentNode.removeChild(node);
26731 //Roo.log(node.tagName);
26732 // remove - but keep children..
26733 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26734 //Roo.log('-- removed');
26735 while (node.childNodes.length) {
26736 var cn = node.childNodes[0];
26737 node.removeChild(cn);
26738 node.parentNode.insertBefore(cn, node);
26739 // move node to parent - and clean it..
26740 this.cleanWord(cn);
26742 node.parentNode.removeChild(node);
26743 /// no need to iterate chidlren = it's got none..
26744 //this.iterateChildren(node, this.cleanWord);
26748 if (node.className.length) {
26750 var cn = node.className.split(/\W+/);
26752 Roo.each(cn, function(cls) {
26753 if (cls.match(/Mso[a-zA-Z]+/)) {
26758 node.className = cna.length ? cna.join(' ') : '';
26760 node.removeAttribute("class");
26764 if (node.hasAttribute("lang")) {
26765 node.removeAttribute("lang");
26768 if (node.hasAttribute("style")) {
26770 var styles = node.getAttribute("style").split(";");
26772 Roo.each(styles, function(s) {
26773 if (!s.match(/:/)) {
26776 var kv = s.split(":");
26777 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26780 // what ever is left... we allow.
26783 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26784 if (!nstyle.length) {
26785 node.removeAttribute('style');
26788 this.iterateChildren(node, this.cleanWord);
26794 * iterateChildren of a Node, calling fn each time, using this as the scole..
26795 * @param {DomNode} node node to iterate children of.
26796 * @param {Function} fn method of this class to call on each item.
26798 iterateChildren : function(node, fn)
26800 if (!node.childNodes.length) {
26803 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26804 fn.call(this, node.childNodes[i])
26810 * cleanTableWidths.
26812 * Quite often pasting from word etc.. results in tables with column and widths.
26813 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26816 cleanTableWidths : function(node)
26821 this.cleanTableWidths(this.doc.body);
26826 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26829 Roo.log(node.tagName);
26830 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26831 this.iterateChildren(node, this.cleanTableWidths);
26834 if (node.hasAttribute('width')) {
26835 node.removeAttribute('width');
26839 if (node.hasAttribute("style")) {
26842 var styles = node.getAttribute("style").split(";");
26844 Roo.each(styles, function(s) {
26845 if (!s.match(/:/)) {
26848 var kv = s.split(":");
26849 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26852 // what ever is left... we allow.
26855 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26856 if (!nstyle.length) {
26857 node.removeAttribute('style');
26861 this.iterateChildren(node, this.cleanTableWidths);
26869 domToHTML : function(currentElement, depth, nopadtext) {
26871 depth = depth || 0;
26872 nopadtext = nopadtext || false;
26874 if (!currentElement) {
26875 return this.domToHTML(this.doc.body);
26878 //Roo.log(currentElement);
26880 var allText = false;
26881 var nodeName = currentElement.nodeName;
26882 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26884 if (nodeName == '#text') {
26886 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26891 if (nodeName != 'BODY') {
26894 // Prints the node tagName, such as <A>, <IMG>, etc
26897 for(i = 0; i < currentElement.attributes.length;i++) {
26899 var aname = currentElement.attributes.item(i).name;
26900 if (!currentElement.attributes.item(i).value.length) {
26903 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26906 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26915 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26918 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26923 // Traverse the tree
26925 var currentElementChild = currentElement.childNodes.item(i);
26926 var allText = true;
26927 var innerHTML = '';
26929 while (currentElementChild) {
26930 // Formatting code (indent the tree so it looks nice on the screen)
26931 var nopad = nopadtext;
26932 if (lastnode == 'SPAN') {
26936 if (currentElementChild.nodeName == '#text') {
26937 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26938 toadd = nopadtext ? toadd : toadd.trim();
26939 if (!nopad && toadd.length > 80) {
26940 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26942 innerHTML += toadd;
26945 currentElementChild = currentElement.childNodes.item(i);
26951 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26953 // Recursively traverse the tree structure of the child node
26954 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26955 lastnode = currentElementChild.nodeName;
26957 currentElementChild=currentElement.childNodes.item(i);
26963 // The remaining code is mostly for formatting the tree
26964 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26969 ret+= "</"+tagName+">";
26975 applyBlacklists : function()
26977 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26978 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26982 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26983 if (b.indexOf(tag) > -1) {
26986 this.white.push(tag);
26990 Roo.each(w, function(tag) {
26991 if (b.indexOf(tag) > -1) {
26994 if (this.white.indexOf(tag) > -1) {
26997 this.white.push(tag);
27002 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27003 if (w.indexOf(tag) > -1) {
27006 this.black.push(tag);
27010 Roo.each(b, function(tag) {
27011 if (w.indexOf(tag) > -1) {
27014 if (this.black.indexOf(tag) > -1) {
27017 this.black.push(tag);
27022 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27023 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27027 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27028 if (b.indexOf(tag) > -1) {
27031 this.cwhite.push(tag);
27035 Roo.each(w, function(tag) {
27036 if (b.indexOf(tag) > -1) {
27039 if (this.cwhite.indexOf(tag) > -1) {
27042 this.cwhite.push(tag);
27047 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27048 if (w.indexOf(tag) > -1) {
27051 this.cblack.push(tag);
27055 Roo.each(b, function(tag) {
27056 if (w.indexOf(tag) > -1) {
27059 if (this.cblack.indexOf(tag) > -1) {
27062 this.cblack.push(tag);
27067 setStylesheets : function(stylesheets)
27069 if(typeof(stylesheets) == 'string'){
27070 Roo.get(this.iframe.contentDocument.head).createChild({
27072 rel : 'stylesheet',
27081 Roo.each(stylesheets, function(s) {
27086 Roo.get(_this.iframe.contentDocument.head).createChild({
27088 rel : 'stylesheet',
27097 removeStylesheets : function()
27101 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27106 setStyle : function(style)
27108 Roo.get(this.iframe.contentDocument.head).createChild({
27117 // hide stuff that is not compatible
27131 * @event specialkey
27135 * @cfg {String} fieldClass @hide
27138 * @cfg {String} focusClass @hide
27141 * @cfg {String} autoCreate @hide
27144 * @cfg {String} inputType @hide
27147 * @cfg {String} invalidClass @hide
27150 * @cfg {String} invalidText @hide
27153 * @cfg {String} msgFx @hide
27156 * @cfg {String} validateOnBlur @hide
27160 Roo.HtmlEditorCore.white = [
27161 'area', 'br', 'img', 'input', 'hr', 'wbr',
27163 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27164 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27165 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27166 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27167 'table', 'ul', 'xmp',
27169 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27172 'dir', 'menu', 'ol', 'ul', 'dl',
27178 Roo.HtmlEditorCore.black = [
27179 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27181 'base', 'basefont', 'bgsound', 'blink', 'body',
27182 'frame', 'frameset', 'head', 'html', 'ilayer',
27183 'iframe', 'layer', 'link', 'meta', 'object',
27184 'script', 'style' ,'title', 'xml' // clean later..
27186 Roo.HtmlEditorCore.clean = [
27187 'script', 'style', 'title', 'xml'
27189 Roo.HtmlEditorCore.remove = [
27194 Roo.HtmlEditorCore.ablack = [
27198 Roo.HtmlEditorCore.aclean = [
27199 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27203 Roo.HtmlEditorCore.pwhite= [
27204 'http', 'https', 'mailto'
27207 // white listed style attributes.
27208 Roo.HtmlEditorCore.cwhite= [
27209 // 'text-align', /// default is to allow most things..
27215 // black listed style attributes.
27216 Roo.HtmlEditorCore.cblack= [
27217 // 'font-size' -- this can be set by the project
27221 Roo.HtmlEditorCore.swapCodes =[
27222 [ 8211, "–" ],
27223 [ 8212, "—" ],
27240 * @class Roo.bootstrap.HtmlEditor
27241 * @extends Roo.bootstrap.TextArea
27242 * Bootstrap HtmlEditor class
27245 * Create a new HtmlEditor
27246 * @param {Object} config The config object
27249 Roo.bootstrap.HtmlEditor = function(config){
27250 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27251 if (!this.toolbars) {
27252 this.toolbars = [];
27255 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27258 * @event initialize
27259 * Fires when the editor is fully initialized (including the iframe)
27260 * @param {HtmlEditor} this
27265 * Fires when the editor is first receives the focus. Any insertion must wait
27266 * until after this event.
27267 * @param {HtmlEditor} this
27271 * @event beforesync
27272 * Fires before the textarea is updated with content from the editor iframe. Return false
27273 * to cancel the sync.
27274 * @param {HtmlEditor} this
27275 * @param {String} html
27279 * @event beforepush
27280 * Fires before the iframe editor is updated with content from the textarea. Return false
27281 * to cancel the push.
27282 * @param {HtmlEditor} this
27283 * @param {String} html
27288 * Fires when the textarea is updated with content from the editor iframe.
27289 * @param {HtmlEditor} this
27290 * @param {String} html
27295 * Fires when the iframe editor is updated with content from the textarea.
27296 * @param {HtmlEditor} this
27297 * @param {String} html
27301 * @event editmodechange
27302 * Fires when the editor switches edit modes
27303 * @param {HtmlEditor} this
27304 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27306 editmodechange: true,
27308 * @event editorevent
27309 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27310 * @param {HtmlEditor} this
27314 * @event firstfocus
27315 * Fires when on first focus - needed by toolbars..
27316 * @param {HtmlEditor} this
27321 * Auto save the htmlEditor value as a file into Events
27322 * @param {HtmlEditor} this
27326 * @event savedpreview
27327 * preview the saved version of htmlEditor
27328 * @param {HtmlEditor} this
27335 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27339 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27344 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27349 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27354 * @cfg {Number} height (in pixels)
27358 * @cfg {Number} width (in pixels)
27363 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27366 stylesheets: false,
27371 // private properties
27372 validationEvent : false,
27374 initialized : false,
27377 onFocus : Roo.emptyFn,
27379 hideMode:'offsets',
27381 tbContainer : false,
27385 toolbarContainer :function() {
27386 return this.wrap.select('.x-html-editor-tb',true).first();
27390 * Protected method that will not generally be called directly. It
27391 * is called when the editor creates its toolbar. Override this method if you need to
27392 * add custom toolbar buttons.
27393 * @param {HtmlEditor} editor
27395 createToolbar : function(){
27396 Roo.log('renewing');
27397 Roo.log("create toolbars");
27399 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27400 this.toolbars[0].render(this.toolbarContainer());
27404 // if (!editor.toolbars || !editor.toolbars.length) {
27405 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27408 // for (var i =0 ; i < editor.toolbars.length;i++) {
27409 // editor.toolbars[i] = Roo.factory(
27410 // typeof(editor.toolbars[i]) == 'string' ?
27411 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27412 // Roo.bootstrap.HtmlEditor);
27413 // editor.toolbars[i].init(editor);
27419 onRender : function(ct, position)
27421 // Roo.log("Call onRender: " + this.xtype);
27423 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27425 this.wrap = this.inputEl().wrap({
27426 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27429 this.editorcore.onRender(ct, position);
27431 if (this.resizable) {
27432 this.resizeEl = new Roo.Resizable(this.wrap, {
27436 minHeight : this.height,
27437 height: this.height,
27438 handles : this.resizable,
27441 resize : function(r, w, h) {
27442 _t.onResize(w,h); // -something
27448 this.createToolbar(this);
27451 if(!this.width && this.resizable){
27452 this.setSize(this.wrap.getSize());
27454 if (this.resizeEl) {
27455 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27456 // should trigger onReize..
27462 onResize : function(w, h)
27464 Roo.log('resize: ' +w + ',' + h );
27465 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27469 if(this.inputEl() ){
27470 if(typeof w == 'number'){
27471 var aw = w - this.wrap.getFrameWidth('lr');
27472 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27475 if(typeof h == 'number'){
27476 var tbh = -11; // fixme it needs to tool bar size!
27477 for (var i =0; i < this.toolbars.length;i++) {
27478 // fixme - ask toolbars for heights?
27479 tbh += this.toolbars[i].el.getHeight();
27480 //if (this.toolbars[i].footer) {
27481 // tbh += this.toolbars[i].footer.el.getHeight();
27489 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27490 ah -= 5; // knock a few pixes off for look..
27491 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27495 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27496 this.editorcore.onResize(ew,eh);
27501 * Toggles the editor between standard and source edit mode.
27502 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27504 toggleSourceEdit : function(sourceEditMode)
27506 this.editorcore.toggleSourceEdit(sourceEditMode);
27508 if(this.editorcore.sourceEditMode){
27509 Roo.log('editor - showing textarea');
27512 // Roo.log(this.syncValue());
27514 this.inputEl().removeClass(['hide', 'x-hidden']);
27515 this.inputEl().dom.removeAttribute('tabIndex');
27516 this.inputEl().focus();
27518 Roo.log('editor - hiding textarea');
27520 // Roo.log(this.pushValue());
27523 this.inputEl().addClass(['hide', 'x-hidden']);
27524 this.inputEl().dom.setAttribute('tabIndex', -1);
27525 //this.deferFocus();
27528 if(this.resizable){
27529 this.setSize(this.wrap.getSize());
27532 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27535 // private (for BoxComponent)
27536 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27538 // private (for BoxComponent)
27539 getResizeEl : function(){
27543 // private (for BoxComponent)
27544 getPositionEl : function(){
27549 initEvents : function(){
27550 this.originalValue = this.getValue();
27554 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27557 // markInvalid : Roo.emptyFn,
27559 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27562 // clearInvalid : Roo.emptyFn,
27564 setValue : function(v){
27565 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27566 this.editorcore.pushValue();
27571 deferFocus : function(){
27572 this.focus.defer(10, this);
27576 focus : function(){
27577 this.editorcore.focus();
27583 onDestroy : function(){
27589 for (var i =0; i < this.toolbars.length;i++) {
27590 // fixme - ask toolbars for heights?
27591 this.toolbars[i].onDestroy();
27594 this.wrap.dom.innerHTML = '';
27595 this.wrap.remove();
27600 onFirstFocus : function(){
27601 //Roo.log("onFirstFocus");
27602 this.editorcore.onFirstFocus();
27603 for (var i =0; i < this.toolbars.length;i++) {
27604 this.toolbars[i].onFirstFocus();
27610 syncValue : function()
27612 this.editorcore.syncValue();
27615 pushValue : function()
27617 this.editorcore.pushValue();
27621 // hide stuff that is not compatible
27635 * @event specialkey
27639 * @cfg {String} fieldClass @hide
27642 * @cfg {String} focusClass @hide
27645 * @cfg {String} autoCreate @hide
27648 * @cfg {String} inputType @hide
27652 * @cfg {String} invalidText @hide
27655 * @cfg {String} msgFx @hide
27658 * @cfg {String} validateOnBlur @hide
27667 Roo.namespace('Roo.bootstrap.htmleditor');
27669 * @class Roo.bootstrap.HtmlEditorToolbar1
27675 new Roo.bootstrap.HtmlEditor({
27678 new Roo.bootstrap.HtmlEditorToolbar1({
27679 disable : { fonts: 1 , format: 1, ..., ... , ...],
27685 * @cfg {Object} disable List of elements to disable..
27686 * @cfg {Array} btns List of additional buttons.
27690 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27693 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27696 Roo.apply(this, config);
27698 // default disabled, based on 'good practice'..
27699 this.disable = this.disable || {};
27700 Roo.applyIf(this.disable, {
27703 specialElements : true
27705 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27707 this.editor = config.editor;
27708 this.editorcore = config.editor.editorcore;
27710 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27712 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27713 // dont call parent... till later.
27715 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27720 editorcore : false,
27725 "h1","h2","h3","h4","h5","h6",
27727 "abbr", "acronym", "address", "cite", "samp", "var",
27731 onRender : function(ct, position)
27733 // Roo.log("Call onRender: " + this.xtype);
27735 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27737 this.el.dom.style.marginBottom = '0';
27739 var editorcore = this.editorcore;
27740 var editor= this.editor;
27743 var btn = function(id,cmd , toggle, handler, html){
27745 var event = toggle ? 'toggle' : 'click';
27750 xns: Roo.bootstrap,
27754 enableToggle:toggle !== false,
27756 pressed : toggle ? false : null,
27759 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27760 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27766 // var cb_box = function...
27771 xns: Roo.bootstrap,
27776 xns: Roo.bootstrap,
27780 Roo.each(this.formats, function(f) {
27781 style.menu.items.push({
27783 xns: Roo.bootstrap,
27784 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27789 editorcore.insertTag(this.tagname);
27796 children.push(style);
27798 btn('bold',false,true);
27799 btn('italic',false,true);
27800 btn('align-left', 'justifyleft',true);
27801 btn('align-center', 'justifycenter',true);
27802 btn('align-right' , 'justifyright',true);
27803 btn('link', false, false, function(btn) {
27804 //Roo.log("create link?");
27805 var url = prompt(this.createLinkText, this.defaultLinkValue);
27806 if(url && url != 'http:/'+'/'){
27807 this.editorcore.relayCmd('createlink', url);
27810 btn('list','insertunorderedlist',true);
27811 btn('pencil', false,true, function(btn){
27813 this.toggleSourceEdit(btn.pressed);
27816 if (this.editor.btns.length > 0) {
27817 for (var i = 0; i<this.editor.btns.length; i++) {
27818 children.push(this.editor.btns[i]);
27826 xns: Roo.bootstrap,
27831 xns: Roo.bootstrap,
27836 cog.menu.items.push({
27838 xns: Roo.bootstrap,
27839 html : Clean styles,
27844 editorcore.insertTag(this.tagname);
27853 this.xtype = 'NavSimplebar';
27855 for(var i=0;i< children.length;i++) {
27857 this.buttons.add(this.addxtypeChild(children[i]));
27861 editor.on('editorevent', this.updateToolbar, this);
27863 onBtnClick : function(id)
27865 this.editorcore.relayCmd(id);
27866 this.editorcore.focus();
27870 * Protected method that will not generally be called directly. It triggers
27871 * a toolbar update by reading the markup state of the current selection in the editor.
27873 updateToolbar: function(){
27875 if(!this.editorcore.activated){
27876 this.editor.onFirstFocus(); // is this neeed?
27880 var btns = this.buttons;
27881 var doc = this.editorcore.doc;
27882 btns.get('bold').setActive(doc.queryCommandState('bold'));
27883 btns.get('italic').setActive(doc.queryCommandState('italic'));
27884 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27886 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27887 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27888 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27890 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27891 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27894 var ans = this.editorcore.getAllAncestors();
27895 if (this.formatCombo) {
27898 var store = this.formatCombo.store;
27899 this.formatCombo.setValue("");
27900 for (var i =0; i < ans.length;i++) {
27901 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27903 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27911 // hides menus... - so this cant be on a menu...
27912 Roo.bootstrap.MenuMgr.hideAll();
27914 Roo.bootstrap.MenuMgr.hideAll();
27915 //this.editorsyncValue();
27917 onFirstFocus: function() {
27918 this.buttons.each(function(item){
27922 toggleSourceEdit : function(sourceEditMode){
27925 if(sourceEditMode){
27926 Roo.log("disabling buttons");
27927 this.buttons.each( function(item){
27928 if(item.cmd != 'pencil'){
27934 Roo.log("enabling buttons");
27935 if(this.editorcore.initialized){
27936 this.buttons.each( function(item){
27942 Roo.log("calling toggole on editor");
27943 // tell the editor that it's been pressed..
27944 this.editor.toggleSourceEdit(sourceEditMode);
27958 * @class Roo.bootstrap.Markdown
27959 * @extends Roo.bootstrap.TextArea
27960 * Bootstrap Showdown editable area
27961 * @cfg {string} content
27964 * Create a new Showdown
27967 Roo.bootstrap.Markdown = function(config){
27968 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27972 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27976 initEvents : function()
27979 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27980 this.markdownEl = this.el.createChild({
27981 cls : 'roo-markdown-area'
27983 this.inputEl().addClass('d-none');
27984 if (this.getValue() == '') {
27985 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27988 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27990 this.markdownEl.on('click', this.toggleTextEdit, this);
27991 this.on('blur', this.toggleTextEdit, this);
27992 this.on('specialkey', this.resizeTextArea, this);
27995 toggleTextEdit : function()
27997 var sh = this.markdownEl.getHeight();
27998 this.inputEl().addClass('d-none');
27999 this.markdownEl.addClass('d-none');
28000 if (!this.editing) {
28002 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28003 this.inputEl().removeClass('d-none');
28004 this.inputEl().focus();
28005 this.editing = true;
28008 // show showdown...
28009 this.updateMarkdown();
28010 this.markdownEl.removeClass('d-none');
28011 this.editing = false;
28014 updateMarkdown : function()
28016 if (this.getValue() == '') {
28017 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28021 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28024 resizeTextArea: function () {
28027 Roo.log([sh, this.getValue().split("\n").length * 30]);
28028 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28030 setValue : function(val)
28032 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28033 if (!this.editing) {
28034 this.updateMarkdown();
28040 if (!this.editing) {
28041 this.toggleTextEdit();
28049 * Ext JS Library 1.1.1
28050 * Copyright(c) 2006-2007, Ext JS, LLC.
28052 * Originally Released Under LGPL - original licence link has changed is not relivant.
28055 * <script type="text/javascript">
28059 * @class Roo.bootstrap.PagingToolbar
28060 * @extends Roo.bootstrap.NavSimplebar
28061 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28063 * Create a new PagingToolbar
28064 * @param {Object} config The config object
28065 * @param {Roo.data.Store} store
28067 Roo.bootstrap.PagingToolbar = function(config)
28069 // old args format still supported... - xtype is prefered..
28070 // created from xtype...
28072 this.ds = config.dataSource;
28074 if (config.store && !this.ds) {
28075 this.store= Roo.factory(config.store, Roo.data);
28076 this.ds = this.store;
28077 this.ds.xmodule = this.xmodule || false;
28080 this.toolbarItems = [];
28081 if (config.items) {
28082 this.toolbarItems = config.items;
28085 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28090 this.bind(this.ds);
28093 if (Roo.bootstrap.version == 4) {
28094 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28096 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28101 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28103 * @cfg {Roo.data.Store} dataSource
28104 * The underlying data store providing the paged data
28107 * @cfg {String/HTMLElement/Element} container
28108 * container The id or element that will contain the toolbar
28111 * @cfg {Boolean} displayInfo
28112 * True to display the displayMsg (defaults to false)
28115 * @cfg {Number} pageSize
28116 * The number of records to display per page (defaults to 20)
28120 * @cfg {String} displayMsg
28121 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28123 displayMsg : 'Displaying {0} - {1} of {2}',
28125 * @cfg {String} emptyMsg
28126 * The message to display when no records are found (defaults to "No data to display")
28128 emptyMsg : 'No data to display',
28130 * Customizable piece of the default paging text (defaults to "Page")
28133 beforePageText : "Page",
28135 * Customizable piece of the default paging text (defaults to "of %0")
28138 afterPageText : "of {0}",
28140 * Customizable piece of the default paging text (defaults to "First Page")
28143 firstText : "First Page",
28145 * Customizable piece of the default paging text (defaults to "Previous Page")
28148 prevText : "Previous Page",
28150 * Customizable piece of the default paging text (defaults to "Next Page")
28153 nextText : "Next Page",
28155 * Customizable piece of the default paging text (defaults to "Last Page")
28158 lastText : "Last Page",
28160 * Customizable piece of the default paging text (defaults to "Refresh")
28163 refreshText : "Refresh",
28167 onRender : function(ct, position)
28169 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28170 this.navgroup.parentId = this.id;
28171 this.navgroup.onRender(this.el, null);
28172 // add the buttons to the navgroup
28174 if(this.displayInfo){
28175 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28176 this.displayEl = this.el.select('.x-paging-info', true).first();
28177 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28178 // this.displayEl = navel.el.select('span',true).first();
28184 Roo.each(_this.buttons, function(e){ // this might need to use render????
28185 Roo.factory(e).render(_this.el);
28189 Roo.each(_this.toolbarItems, function(e) {
28190 _this.navgroup.addItem(e);
28194 this.first = this.navgroup.addItem({
28195 tooltip: this.firstText,
28196 cls: "prev btn-outline-secondary",
28197 html : ' <i class="fa fa-step-backward"></i>',
28199 preventDefault: true,
28200 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28203 this.prev = this.navgroup.addItem({
28204 tooltip: this.prevText,
28205 cls: "prev btn-outline-secondary",
28206 html : ' <i class="fa fa-backward"></i>',
28208 preventDefault: true,
28209 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28211 //this.addSeparator();
28214 var field = this.navgroup.addItem( {
28216 cls : 'x-paging-position btn-outline-secondary',
28218 html : this.beforePageText +
28219 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28220 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28223 this.field = field.el.select('input', true).first();
28224 this.field.on("keydown", this.onPagingKeydown, this);
28225 this.field.on("focus", function(){this.dom.select();});
28228 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28229 //this.field.setHeight(18);
28230 //this.addSeparator();
28231 this.next = this.navgroup.addItem({
28232 tooltip: this.nextText,
28233 cls: "next btn-outline-secondary",
28234 html : ' <i class="fa fa-forward"></i>',
28236 preventDefault: true,
28237 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28239 this.last = this.navgroup.addItem({
28240 tooltip: this.lastText,
28241 html : ' <i class="fa fa-step-forward"></i>',
28242 cls: "next btn-outline-secondary",
28244 preventDefault: true,
28245 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28247 //this.addSeparator();
28248 this.loading = this.navgroup.addItem({
28249 tooltip: this.refreshText,
28250 cls: "btn-outline-secondary",
28251 html : ' <i class="fa fa-refresh"></i>',
28252 preventDefault: true,
28253 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28259 updateInfo : function(){
28260 if(this.displayEl){
28261 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28262 var msg = count == 0 ?
28266 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28268 this.displayEl.update(msg);
28273 onLoad : function(ds, r, o)
28275 this.cursor = o.params && o.params.start ? o.params.start : 0;
28277 var d = this.getPageData(),
28282 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28283 this.field.dom.value = ap;
28284 this.first.setDisabled(ap == 1);
28285 this.prev.setDisabled(ap == 1);
28286 this.next.setDisabled(ap == ps);
28287 this.last.setDisabled(ap == ps);
28288 this.loading.enable();
28293 getPageData : function(){
28294 var total = this.ds.getTotalCount();
28297 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28298 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28303 onLoadError : function(){
28304 this.loading.enable();
28308 onPagingKeydown : function(e){
28309 var k = e.getKey();
28310 var d = this.getPageData();
28312 var v = this.field.dom.value, pageNum;
28313 if(!v || isNaN(pageNum = parseInt(v, 10))){
28314 this.field.dom.value = d.activePage;
28317 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28318 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28321 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))
28323 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28324 this.field.dom.value = pageNum;
28325 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28328 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28330 var v = this.field.dom.value, pageNum;
28331 var increment = (e.shiftKey) ? 10 : 1;
28332 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28335 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28336 this.field.dom.value = d.activePage;
28339 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28341 this.field.dom.value = parseInt(v, 10) + increment;
28342 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28343 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28350 beforeLoad : function(){
28352 this.loading.disable();
28357 onClick : function(which){
28366 ds.load({params:{start: 0, limit: this.pageSize}});
28369 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28372 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28375 var total = ds.getTotalCount();
28376 var extra = total % this.pageSize;
28377 var lastStart = extra ? (total - extra) : total-this.pageSize;
28378 ds.load({params:{start: lastStart, limit: this.pageSize}});
28381 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28387 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28388 * @param {Roo.data.Store} store The data store to unbind
28390 unbind : function(ds){
28391 ds.un("beforeload", this.beforeLoad, this);
28392 ds.un("load", this.onLoad, this);
28393 ds.un("loadexception", this.onLoadError, this);
28394 ds.un("remove", this.updateInfo, this);
28395 ds.un("add", this.updateInfo, this);
28396 this.ds = undefined;
28400 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28401 * @param {Roo.data.Store} store The data store to bind
28403 bind : function(ds){
28404 ds.on("beforeload", this.beforeLoad, this);
28405 ds.on("load", this.onLoad, this);
28406 ds.on("loadexception", this.onLoadError, this);
28407 ds.on("remove", this.updateInfo, this);
28408 ds.on("add", this.updateInfo, this);
28419 * @class Roo.bootstrap.MessageBar
28420 * @extends Roo.bootstrap.Component
28421 * Bootstrap MessageBar class
28422 * @cfg {String} html contents of the MessageBar
28423 * @cfg {String} weight (info | success | warning | danger) default info
28424 * @cfg {String} beforeClass insert the bar before the given class
28425 * @cfg {Boolean} closable (true | false) default false
28426 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28429 * Create a new Element
28430 * @param {Object} config The config object
28433 Roo.bootstrap.MessageBar = function(config){
28434 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28437 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28443 beforeClass: 'bootstrap-sticky-wrap',
28445 getAutoCreate : function(){
28449 cls: 'alert alert-dismissable alert-' + this.weight,
28454 html: this.html || ''
28460 cfg.cls += ' alert-messages-fixed';
28474 onRender : function(ct, position)
28476 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28479 var cfg = Roo.apply({}, this.getAutoCreate());
28483 cfg.cls += ' ' + this.cls;
28486 cfg.style = this.style;
28488 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28490 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28493 this.el.select('>button.close').on('click', this.hide, this);
28499 if (!this.rendered) {
28505 this.fireEvent('show', this);
28511 if (!this.rendered) {
28517 this.fireEvent('hide', this);
28520 update : function()
28522 // var e = this.el.dom.firstChild;
28524 // if(this.closable){
28525 // e = e.nextSibling;
28528 // e.data = this.html || '';
28530 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28546 * @class Roo.bootstrap.Graph
28547 * @extends Roo.bootstrap.Component
28548 * Bootstrap Graph class
28552 @cfg {String} graphtype bar | vbar | pie
28553 @cfg {number} g_x coodinator | centre x (pie)
28554 @cfg {number} g_y coodinator | centre y (pie)
28555 @cfg {number} g_r radius (pie)
28556 @cfg {number} g_height height of the chart (respected by all elements in the set)
28557 @cfg {number} g_width width of the chart (respected by all elements in the set)
28558 @cfg {Object} title The title of the chart
28561 -opts (object) options for the chart
28563 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28564 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28566 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.
28567 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28569 o stretch (boolean)
28571 -opts (object) options for the pie
28574 o startAngle (number)
28575 o endAngle (number)
28579 * Create a new Input
28580 * @param {Object} config The config object
28583 Roo.bootstrap.Graph = function(config){
28584 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28590 * The img click event for the img.
28591 * @param {Roo.EventObject} e
28597 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28608 //g_colors: this.colors,
28615 getAutoCreate : function(){
28626 onRender : function(ct,position){
28629 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28631 if (typeof(Raphael) == 'undefined') {
28632 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28636 this.raphael = Raphael(this.el.dom);
28638 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28639 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28640 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28641 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28643 r.text(160, 10, "Single Series Chart").attr(txtattr);
28644 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28645 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28646 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28648 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28649 r.barchart(330, 10, 300, 220, data1);
28650 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28651 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28654 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28655 // r.barchart(30, 30, 560, 250, xdata, {
28656 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28657 // axis : "0 0 1 1",
28658 // axisxlabels : xdata
28659 // //yvalues : cols,
28662 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28664 // this.load(null,xdata,{
28665 // axis : "0 0 1 1",
28666 // axisxlabels : xdata
28671 load : function(graphtype,xdata,opts)
28673 this.raphael.clear();
28675 graphtype = this.graphtype;
28680 var r = this.raphael,
28681 fin = function () {
28682 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28684 fout = function () {
28685 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28687 pfin = function() {
28688 this.sector.stop();
28689 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28692 this.label[0].stop();
28693 this.label[0].attr({ r: 7.5 });
28694 this.label[1].attr({ "font-weight": 800 });
28697 pfout = function() {
28698 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28701 this.label[0].animate({ r: 5 }, 500, "bounce");
28702 this.label[1].attr({ "font-weight": 400 });
28708 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28711 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28714 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28715 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28717 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28724 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28729 setTitle: function(o)
28734 initEvents: function() {
28737 this.el.on('click', this.onClick, this);
28741 onClick : function(e)
28743 Roo.log('img onclick');
28744 this.fireEvent('click', this, e);
28756 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28759 * @class Roo.bootstrap.dash.NumberBox
28760 * @extends Roo.bootstrap.Component
28761 * Bootstrap NumberBox class
28762 * @cfg {String} headline Box headline
28763 * @cfg {String} content Box content
28764 * @cfg {String} icon Box icon
28765 * @cfg {String} footer Footer text
28766 * @cfg {String} fhref Footer href
28769 * Create a new NumberBox
28770 * @param {Object} config The config object
28774 Roo.bootstrap.dash.NumberBox = function(config){
28775 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28779 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28788 getAutoCreate : function(){
28792 cls : 'small-box ',
28800 cls : 'roo-headline',
28801 html : this.headline
28805 cls : 'roo-content',
28806 html : this.content
28820 cls : 'ion ' + this.icon
28829 cls : 'small-box-footer',
28830 href : this.fhref || '#',
28834 cfg.cn.push(footer);
28841 onRender : function(ct,position){
28842 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28849 setHeadline: function (value)
28851 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28854 setFooter: function (value, href)
28856 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28859 this.el.select('a.small-box-footer',true).first().attr('href', href);
28864 setContent: function (value)
28866 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28869 initEvents: function()
28883 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28886 * @class Roo.bootstrap.dash.TabBox
28887 * @extends Roo.bootstrap.Component
28888 * Bootstrap TabBox class
28889 * @cfg {String} title Title of the TabBox
28890 * @cfg {String} icon Icon of the TabBox
28891 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28892 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28895 * Create a new TabBox
28896 * @param {Object} config The config object
28900 Roo.bootstrap.dash.TabBox = function(config){
28901 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28906 * When a pane is added
28907 * @param {Roo.bootstrap.dash.TabPane} pane
28911 * @event activatepane
28912 * When a pane is activated
28913 * @param {Roo.bootstrap.dash.TabPane} pane
28915 "activatepane" : true
28923 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28928 tabScrollable : false,
28930 getChildContainer : function()
28932 return this.el.select('.tab-content', true).first();
28935 getAutoCreate : function(){
28939 cls: 'pull-left header',
28947 cls: 'fa ' + this.icon
28953 cls: 'nav nav-tabs pull-right',
28959 if(this.tabScrollable){
28966 cls: 'nav nav-tabs pull-right',
28977 cls: 'nav-tabs-custom',
28982 cls: 'tab-content no-padding',
28990 initEvents : function()
28992 //Roo.log('add add pane handler');
28993 this.on('addpane', this.onAddPane, this);
28996 * Updates the box title
28997 * @param {String} html to set the title to.
28999 setTitle : function(value)
29001 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29003 onAddPane : function(pane)
29005 this.panes.push(pane);
29006 //Roo.log('addpane');
29008 // tabs are rendere left to right..
29009 if(!this.showtabs){
29013 var ctr = this.el.select('.nav-tabs', true).first();
29016 var existing = ctr.select('.nav-tab',true);
29017 var qty = existing.getCount();;
29020 var tab = ctr.createChild({
29022 cls : 'nav-tab' + (qty ? '' : ' active'),
29030 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29033 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29035 pane.el.addClass('active');
29040 onTabClick : function(ev,un,ob,pane)
29042 //Roo.log('tab - prev default');
29043 ev.preventDefault();
29046 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29047 pane.tab.addClass('active');
29048 //Roo.log(pane.title);
29049 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29050 // technically we should have a deactivate event.. but maybe add later.
29051 // and it should not de-activate the selected tab...
29052 this.fireEvent('activatepane', pane);
29053 pane.el.addClass('active');
29054 pane.fireEvent('activate');
29059 getActivePane : function()
29062 Roo.each(this.panes, function(p) {
29063 if(p.el.hasClass('active')){
29084 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29086 * @class Roo.bootstrap.TabPane
29087 * @extends Roo.bootstrap.Component
29088 * Bootstrap TabPane class
29089 * @cfg {Boolean} active (false | true) Default false
29090 * @cfg {String} title title of panel
29094 * Create a new TabPane
29095 * @param {Object} config The config object
29098 Roo.bootstrap.dash.TabPane = function(config){
29099 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29105 * When a pane is activated
29106 * @param {Roo.bootstrap.dash.TabPane} pane
29113 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29118 // the tabBox that this is attached to.
29121 getAutoCreate : function()
29129 cfg.cls += ' active';
29134 initEvents : function()
29136 //Roo.log('trigger add pane handler');
29137 this.parent().fireEvent('addpane', this)
29141 * Updates the tab title
29142 * @param {String} html to set the title to.
29144 setTitle: function(str)
29150 this.tab.select('a', true).first().dom.innerHTML = str;
29167 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29170 * @class Roo.bootstrap.menu.Menu
29171 * @extends Roo.bootstrap.Component
29172 * Bootstrap Menu class - container for Menu
29173 * @cfg {String} html Text of the menu
29174 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29175 * @cfg {String} icon Font awesome icon
29176 * @cfg {String} pos Menu align to (top | bottom) default bottom
29180 * Create a new Menu
29181 * @param {Object} config The config object
29185 Roo.bootstrap.menu.Menu = function(config){
29186 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29190 * @event beforeshow
29191 * Fires before this menu is displayed
29192 * @param {Roo.bootstrap.menu.Menu} this
29196 * @event beforehide
29197 * Fires before this menu is hidden
29198 * @param {Roo.bootstrap.menu.Menu} this
29203 * Fires after this menu is displayed
29204 * @param {Roo.bootstrap.menu.Menu} this
29209 * Fires after this menu is hidden
29210 * @param {Roo.bootstrap.menu.Menu} this
29215 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29216 * @param {Roo.bootstrap.menu.Menu} this
29217 * @param {Roo.EventObject} e
29224 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29228 weight : 'default',
29233 getChildContainer : function() {
29234 if(this.isSubMenu){
29238 return this.el.select('ul.dropdown-menu', true).first();
29241 getAutoCreate : function()
29246 cls : 'roo-menu-text',
29254 cls : 'fa ' + this.icon
29265 cls : 'dropdown-button btn btn-' + this.weight,
29270 cls : 'dropdown-toggle btn btn-' + this.weight,
29280 cls : 'dropdown-menu'
29286 if(this.pos == 'top'){
29287 cfg.cls += ' dropup';
29290 if(this.isSubMenu){
29293 cls : 'dropdown-menu'
29300 onRender : function(ct, position)
29302 this.isSubMenu = ct.hasClass('dropdown-submenu');
29304 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29307 initEvents : function()
29309 if(this.isSubMenu){
29313 this.hidden = true;
29315 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29316 this.triggerEl.on('click', this.onTriggerPress, this);
29318 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29319 this.buttonEl.on('click', this.onClick, this);
29325 if(this.isSubMenu){
29329 return this.el.select('ul.dropdown-menu', true).first();
29332 onClick : function(e)
29334 this.fireEvent("click", this, e);
29337 onTriggerPress : function(e)
29339 if (this.isVisible()) {
29346 isVisible : function(){
29347 return !this.hidden;
29352 this.fireEvent("beforeshow", this);
29354 this.hidden = false;
29355 this.el.addClass('open');
29357 Roo.get(document).on("mouseup", this.onMouseUp, this);
29359 this.fireEvent("show", this);
29366 this.fireEvent("beforehide", this);
29368 this.hidden = true;
29369 this.el.removeClass('open');
29371 Roo.get(document).un("mouseup", this.onMouseUp);
29373 this.fireEvent("hide", this);
29376 onMouseUp : function()
29390 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29393 * @class Roo.bootstrap.menu.Item
29394 * @extends Roo.bootstrap.Component
29395 * Bootstrap MenuItem class
29396 * @cfg {Boolean} submenu (true | false) default false
29397 * @cfg {String} html text of the item
29398 * @cfg {String} href the link
29399 * @cfg {Boolean} disable (true | false) default false
29400 * @cfg {Boolean} preventDefault (true | false) default true
29401 * @cfg {String} icon Font awesome icon
29402 * @cfg {String} pos Submenu align to (left | right) default right
29406 * Create a new Item
29407 * @param {Object} config The config object
29411 Roo.bootstrap.menu.Item = function(config){
29412 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29416 * Fires when the mouse is hovering over this menu
29417 * @param {Roo.bootstrap.menu.Item} this
29418 * @param {Roo.EventObject} e
29423 * Fires when the mouse exits this menu
29424 * @param {Roo.bootstrap.menu.Item} this
29425 * @param {Roo.EventObject} e
29431 * The raw click event for the entire grid.
29432 * @param {Roo.EventObject} e
29438 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29443 preventDefault: true,
29448 getAutoCreate : function()
29453 cls : 'roo-menu-item-text',
29461 cls : 'fa ' + this.icon
29470 href : this.href || '#',
29477 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29481 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29483 if(this.pos == 'left'){
29484 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29491 initEvents : function()
29493 this.el.on('mouseover', this.onMouseOver, this);
29494 this.el.on('mouseout', this.onMouseOut, this);
29496 this.el.select('a', true).first().on('click', this.onClick, this);
29500 onClick : function(e)
29502 if(this.preventDefault){
29503 e.preventDefault();
29506 this.fireEvent("click", this, e);
29509 onMouseOver : function(e)
29511 if(this.submenu && this.pos == 'left'){
29512 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29515 this.fireEvent("mouseover", this, e);
29518 onMouseOut : function(e)
29520 this.fireEvent("mouseout", this, e);
29532 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29535 * @class Roo.bootstrap.menu.Separator
29536 * @extends Roo.bootstrap.Component
29537 * Bootstrap Separator class
29540 * Create a new Separator
29541 * @param {Object} config The config object
29545 Roo.bootstrap.menu.Separator = function(config){
29546 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29549 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29551 getAutoCreate : function(){
29554 cls: 'dropdown-divider divider'
29572 * @class Roo.bootstrap.Tooltip
29573 * Bootstrap Tooltip class
29574 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29575 * to determine which dom element triggers the tooltip.
29577 * It needs to add support for additional attributes like tooltip-position
29580 * Create a new Toolti
29581 * @param {Object} config The config object
29584 Roo.bootstrap.Tooltip = function(config){
29585 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29587 this.alignment = Roo.bootstrap.Tooltip.alignment;
29589 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29590 this.alignment = config.alignment;
29595 Roo.apply(Roo.bootstrap.Tooltip, {
29597 * @function init initialize tooltip monitoring.
29601 currentTip : false,
29602 currentRegion : false,
29608 Roo.get(document).on('mouseover', this.enter ,this);
29609 Roo.get(document).on('mouseout', this.leave, this);
29612 this.currentTip = new Roo.bootstrap.Tooltip();
29615 enter : function(ev)
29617 var dom = ev.getTarget();
29619 //Roo.log(['enter',dom]);
29620 var el = Roo.fly(dom);
29621 if (this.currentEl) {
29623 //Roo.log(this.currentEl);
29624 //Roo.log(this.currentEl.contains(dom));
29625 if (this.currentEl == el) {
29628 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29634 if (this.currentTip.el) {
29635 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29639 if(!el || el.dom == document){
29645 if (!el.attr('tooltip')) {
29646 pel = el.findParent("[tooltip]");
29648 bindEl = Roo.get(pel);
29654 // you can not look for children, as if el is the body.. then everythign is the child..
29655 if (!pel && !el.attr('tooltip')) { //
29656 if (!el.select("[tooltip]").elements.length) {
29659 // is the mouse over this child...?
29660 bindEl = el.select("[tooltip]").first();
29661 var xy = ev.getXY();
29662 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29663 //Roo.log("not in region.");
29666 //Roo.log("child element over..");
29669 this.currentEl = el;
29670 this.currentTip.bind(bindEl);
29671 this.currentRegion = Roo.lib.Region.getRegion(dom);
29672 this.currentTip.enter();
29675 leave : function(ev)
29677 var dom = ev.getTarget();
29678 //Roo.log(['leave',dom]);
29679 if (!this.currentEl) {
29684 if (dom != this.currentEl.dom) {
29687 var xy = ev.getXY();
29688 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29691 // only activate leave if mouse cursor is outside... bounding box..
29696 if (this.currentTip) {
29697 this.currentTip.leave();
29699 //Roo.log('clear currentEl');
29700 this.currentEl = false;
29705 'left' : ['r-l', [-2,0], 'right'],
29706 'right' : ['l-r', [2,0], 'left'],
29707 'bottom' : ['t-b', [0,2], 'top'],
29708 'top' : [ 'b-t', [0,-2], 'bottom']
29714 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29719 delay : null, // can be { show : 300 , hide: 500}
29723 hoverState : null, //???
29725 placement : 'bottom',
29729 getAutoCreate : function(){
29736 cls : 'tooltip-arrow arrow'
29739 cls : 'tooltip-inner'
29746 bind : function(el)
29751 initEvents : function()
29753 this.arrowEl = this.el.select('.arrow', true).first();
29754 this.innerEl = this.el.select('.tooltip-inner', true).first();
29757 enter : function () {
29759 if (this.timeout != null) {
29760 clearTimeout(this.timeout);
29763 this.hoverState = 'in';
29764 //Roo.log("enter - show");
29765 if (!this.delay || !this.delay.show) {
29770 this.timeout = setTimeout(function () {
29771 if (_t.hoverState == 'in') {
29774 }, this.delay.show);
29778 clearTimeout(this.timeout);
29780 this.hoverState = 'out';
29781 if (!this.delay || !this.delay.hide) {
29787 this.timeout = setTimeout(function () {
29788 //Roo.log("leave - timeout");
29790 if (_t.hoverState == 'out') {
29792 Roo.bootstrap.Tooltip.currentEl = false;
29797 show : function (msg)
29800 this.render(document.body);
29803 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29805 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29807 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29809 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29810 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29812 var placement = typeof this.placement == 'function' ?
29813 this.placement.call(this, this.el, on_el) :
29816 var autoToken = /\s?auto?\s?/i;
29817 var autoPlace = autoToken.test(placement);
29819 placement = placement.replace(autoToken, '') || 'top';
29823 //this.el.setXY([0,0]);
29825 //this.el.dom.style.display='block';
29827 //this.el.appendTo(on_el);
29829 var p = this.getPosition();
29830 var box = this.el.getBox();
29836 var align = this.alignment[placement];
29838 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29840 if(placement == 'top' || placement == 'bottom'){
29842 placement = 'right';
29845 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29846 placement = 'left';
29849 var scroll = Roo.select('body', true).first().getScroll();
29851 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29855 align = this.alignment[placement];
29857 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29861 var elems = document.getElementsByTagName('div');
29862 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29863 for (var i = 0; i < elems.length; i++) {
29864 var zindex = Number.parseInt(
29865 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29868 if (zindex > highest) {
29875 this.el.dom.style.zIndex = highest;
29877 this.el.alignTo(this.bindEl, align[0],align[1]);
29878 //var arrow = this.el.select('.arrow',true).first();
29879 //arrow.set(align[2],
29881 this.el.addClass(placement);
29882 this.el.addClass("bs-tooltip-"+ placement);
29884 this.el.addClass('in fade show');
29886 this.hoverState = null;
29888 if (this.el.hasClass('fade')) {
29903 //this.el.setXY([0,0]);
29904 this.el.removeClass(['show', 'in']);
29920 * @class Roo.bootstrap.LocationPicker
29921 * @extends Roo.bootstrap.Component
29922 * Bootstrap LocationPicker class
29923 * @cfg {Number} latitude Position when init default 0
29924 * @cfg {Number} longitude Position when init default 0
29925 * @cfg {Number} zoom default 15
29926 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29927 * @cfg {Boolean} mapTypeControl default false
29928 * @cfg {Boolean} disableDoubleClickZoom default false
29929 * @cfg {Boolean} scrollwheel default true
29930 * @cfg {Boolean} streetViewControl default false
29931 * @cfg {Number} radius default 0
29932 * @cfg {String} locationName
29933 * @cfg {Boolean} draggable default true
29934 * @cfg {Boolean} enableAutocomplete default false
29935 * @cfg {Boolean} enableReverseGeocode default true
29936 * @cfg {String} markerTitle
29939 * Create a new LocationPicker
29940 * @param {Object} config The config object
29944 Roo.bootstrap.LocationPicker = function(config){
29946 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29951 * Fires when the picker initialized.
29952 * @param {Roo.bootstrap.LocationPicker} this
29953 * @param {Google Location} location
29957 * @event positionchanged
29958 * Fires when the picker position changed.
29959 * @param {Roo.bootstrap.LocationPicker} this
29960 * @param {Google Location} location
29962 positionchanged : true,
29965 * Fires when the map resize.
29966 * @param {Roo.bootstrap.LocationPicker} this
29971 * Fires when the map show.
29972 * @param {Roo.bootstrap.LocationPicker} this
29977 * Fires when the map hide.
29978 * @param {Roo.bootstrap.LocationPicker} this
29983 * Fires when click the map.
29984 * @param {Roo.bootstrap.LocationPicker} this
29985 * @param {Map event} e
29989 * @event mapRightClick
29990 * Fires when right click the map.
29991 * @param {Roo.bootstrap.LocationPicker} this
29992 * @param {Map event} e
29994 mapRightClick : true,
29996 * @event markerClick
29997 * Fires when click the marker.
29998 * @param {Roo.bootstrap.LocationPicker} this
29999 * @param {Map event} e
30001 markerClick : true,
30003 * @event markerRightClick
30004 * Fires when right click the marker.
30005 * @param {Roo.bootstrap.LocationPicker} this
30006 * @param {Map event} e
30008 markerRightClick : true,
30010 * @event OverlayViewDraw
30011 * Fires when OverlayView Draw
30012 * @param {Roo.bootstrap.LocationPicker} this
30014 OverlayViewDraw : true,
30016 * @event OverlayViewOnAdd
30017 * Fires when OverlayView Draw
30018 * @param {Roo.bootstrap.LocationPicker} this
30020 OverlayViewOnAdd : true,
30022 * @event OverlayViewOnRemove
30023 * Fires when OverlayView Draw
30024 * @param {Roo.bootstrap.LocationPicker} this
30026 OverlayViewOnRemove : true,
30028 * @event OverlayViewShow
30029 * Fires when OverlayView Draw
30030 * @param {Roo.bootstrap.LocationPicker} this
30031 * @param {Pixel} cpx
30033 OverlayViewShow : true,
30035 * @event OverlayViewHide
30036 * Fires when OverlayView Draw
30037 * @param {Roo.bootstrap.LocationPicker} this
30039 OverlayViewHide : true,
30041 * @event loadexception
30042 * Fires when load google lib failed.
30043 * @param {Roo.bootstrap.LocationPicker} this
30045 loadexception : true
30050 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30052 gMapContext: false,
30058 mapTypeControl: false,
30059 disableDoubleClickZoom: false,
30061 streetViewControl: false,
30065 enableAutocomplete: false,
30066 enableReverseGeocode: true,
30069 getAutoCreate: function()
30074 cls: 'roo-location-picker'
30080 initEvents: function(ct, position)
30082 if(!this.el.getWidth() || this.isApplied()){
30086 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30091 initial: function()
30093 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30094 this.fireEvent('loadexception', this);
30098 if(!this.mapTypeId){
30099 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30102 this.gMapContext = this.GMapContext();
30104 this.initOverlayView();
30106 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30110 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30111 _this.setPosition(_this.gMapContext.marker.position);
30114 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30115 _this.fireEvent('mapClick', this, event);
30119 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30120 _this.fireEvent('mapRightClick', this, event);
30124 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30125 _this.fireEvent('markerClick', this, event);
30129 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30130 _this.fireEvent('markerRightClick', this, event);
30134 this.setPosition(this.gMapContext.location);
30136 this.fireEvent('initial', this, this.gMapContext.location);
30139 initOverlayView: function()
30143 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30147 _this.fireEvent('OverlayViewDraw', _this);
30152 _this.fireEvent('OverlayViewOnAdd', _this);
30155 onRemove: function()
30157 _this.fireEvent('OverlayViewOnRemove', _this);
30160 show: function(cpx)
30162 _this.fireEvent('OverlayViewShow', _this, cpx);
30167 _this.fireEvent('OverlayViewHide', _this);
30173 fromLatLngToContainerPixel: function(event)
30175 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30178 isApplied: function()
30180 return this.getGmapContext() == false ? false : true;
30183 getGmapContext: function()
30185 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30188 GMapContext: function()
30190 var position = new google.maps.LatLng(this.latitude, this.longitude);
30192 var _map = new google.maps.Map(this.el.dom, {
30195 mapTypeId: this.mapTypeId,
30196 mapTypeControl: this.mapTypeControl,
30197 disableDoubleClickZoom: this.disableDoubleClickZoom,
30198 scrollwheel: this.scrollwheel,
30199 streetViewControl: this.streetViewControl,
30200 locationName: this.locationName,
30201 draggable: this.draggable,
30202 enableAutocomplete: this.enableAutocomplete,
30203 enableReverseGeocode: this.enableReverseGeocode
30206 var _marker = new google.maps.Marker({
30207 position: position,
30209 title: this.markerTitle,
30210 draggable: this.draggable
30217 location: position,
30218 radius: this.radius,
30219 locationName: this.locationName,
30220 addressComponents: {
30221 formatted_address: null,
30222 addressLine1: null,
30223 addressLine2: null,
30225 streetNumber: null,
30229 stateOrProvince: null
30232 domContainer: this.el.dom,
30233 geodecoder: new google.maps.Geocoder()
30237 drawCircle: function(center, radius, options)
30239 if (this.gMapContext.circle != null) {
30240 this.gMapContext.circle.setMap(null);
30244 options = Roo.apply({}, options, {
30245 strokeColor: "#0000FF",
30246 strokeOpacity: .35,
30248 fillColor: "#0000FF",
30252 options.map = this.gMapContext.map;
30253 options.radius = radius;
30254 options.center = center;
30255 this.gMapContext.circle = new google.maps.Circle(options);
30256 return this.gMapContext.circle;
30262 setPosition: function(location)
30264 this.gMapContext.location = location;
30265 this.gMapContext.marker.setPosition(location);
30266 this.gMapContext.map.panTo(location);
30267 this.drawCircle(location, this.gMapContext.radius, {});
30271 if (this.gMapContext.settings.enableReverseGeocode) {
30272 this.gMapContext.geodecoder.geocode({
30273 latLng: this.gMapContext.location
30274 }, function(results, status) {
30276 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30277 _this.gMapContext.locationName = results[0].formatted_address;
30278 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30280 _this.fireEvent('positionchanged', this, location);
30287 this.fireEvent('positionchanged', this, location);
30292 google.maps.event.trigger(this.gMapContext.map, "resize");
30294 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30296 this.fireEvent('resize', this);
30299 setPositionByLatLng: function(latitude, longitude)
30301 this.setPosition(new google.maps.LatLng(latitude, longitude));
30304 getCurrentPosition: function()
30307 latitude: this.gMapContext.location.lat(),
30308 longitude: this.gMapContext.location.lng()
30312 getAddressName: function()
30314 return this.gMapContext.locationName;
30317 getAddressComponents: function()
30319 return this.gMapContext.addressComponents;
30322 address_component_from_google_geocode: function(address_components)
30326 for (var i = 0; i < address_components.length; i++) {
30327 var component = address_components[i];
30328 if (component.types.indexOf("postal_code") >= 0) {
30329 result.postalCode = component.short_name;
30330 } else if (component.types.indexOf("street_number") >= 0) {
30331 result.streetNumber = component.short_name;
30332 } else if (component.types.indexOf("route") >= 0) {
30333 result.streetName = component.short_name;
30334 } else if (component.types.indexOf("neighborhood") >= 0) {
30335 result.city = component.short_name;
30336 } else if (component.types.indexOf("locality") >= 0) {
30337 result.city = component.short_name;
30338 } else if (component.types.indexOf("sublocality") >= 0) {
30339 result.district = component.short_name;
30340 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30341 result.stateOrProvince = component.short_name;
30342 } else if (component.types.indexOf("country") >= 0) {
30343 result.country = component.short_name;
30347 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30348 result.addressLine2 = "";
30352 setZoomLevel: function(zoom)
30354 this.gMapContext.map.setZoom(zoom);
30367 this.fireEvent('show', this);
30378 this.fireEvent('hide', this);
30383 Roo.apply(Roo.bootstrap.LocationPicker, {
30385 OverlayView : function(map, options)
30387 options = options || {};
30394 * @class Roo.bootstrap.Alert
30395 * @extends Roo.bootstrap.Component
30396 * Bootstrap Alert class - shows an alert area box
30398 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30399 Enter a valid email address
30402 * @cfg {String} title The title of alert
30403 * @cfg {String} html The content of alert
30404 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30405 * @cfg {String} fa font-awesomeicon
30406 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30407 * @cfg {Boolean} close true to show a x closer
30411 * Create a new alert
30412 * @param {Object} config The config object
30416 Roo.bootstrap.Alert = function(config){
30417 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30421 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30427 faicon: false, // BC
30431 getAutoCreate : function()
30443 style : this.close ? '' : 'display:none'
30447 cls : 'roo-alert-icon'
30452 cls : 'roo-alert-title',
30457 cls : 'roo-alert-text',
30464 cfg.cn[0].cls += ' fa ' + this.faicon;
30467 cfg.cn[0].cls += ' fa ' + this.fa;
30471 cfg.cls += ' alert-' + this.weight;
30477 initEvents: function()
30479 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30480 this.titleEl = this.el.select('.roo-alert-title',true).first();
30481 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30482 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30483 if (this.seconds > 0) {
30484 this.hide.defer(this.seconds, this);
30488 * Set the Title Message HTML
30489 * @param {String} html
30491 setTitle : function(str)
30493 this.titleEl.dom.innerHTML = str;
30497 * Set the Body Message HTML
30498 * @param {String} html
30500 setHtml : function(str)
30502 this.htmlEl.dom.innerHTML = str;
30505 * Set the Weight of the alert
30506 * @param {String} (success|info|warning|danger) weight
30509 setWeight : function(weight)
30512 this.el.removeClass('alert-' + this.weight);
30515 this.weight = weight;
30517 this.el.addClass('alert-' + this.weight);
30520 * Set the Icon of the alert
30521 * @param {String} see fontawsome names (name without the 'fa-' bit)
30523 setIcon : function(icon)
30526 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30529 this.faicon = icon;
30531 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30556 * @class Roo.bootstrap.UploadCropbox
30557 * @extends Roo.bootstrap.Component
30558 * Bootstrap UploadCropbox class
30559 * @cfg {String} emptyText show when image has been loaded
30560 * @cfg {String} rotateNotify show when image too small to rotate
30561 * @cfg {Number} errorTimeout default 3000
30562 * @cfg {Number} minWidth default 300
30563 * @cfg {Number} minHeight default 300
30564 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30565 * @cfg {Boolean} isDocument (true|false) default false
30566 * @cfg {String} url action url
30567 * @cfg {String} paramName default 'imageUpload'
30568 * @cfg {String} method default POST
30569 * @cfg {Boolean} loadMask (true|false) default true
30570 * @cfg {Boolean} loadingText default 'Loading...'
30573 * Create a new UploadCropbox
30574 * @param {Object} config The config object
30577 Roo.bootstrap.UploadCropbox = function(config){
30578 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30582 * @event beforeselectfile
30583 * Fire before select file
30584 * @param {Roo.bootstrap.UploadCropbox} this
30586 "beforeselectfile" : true,
30589 * Fire after initEvent
30590 * @param {Roo.bootstrap.UploadCropbox} this
30595 * Fire after initEvent
30596 * @param {Roo.bootstrap.UploadCropbox} this
30597 * @param {String} data
30602 * Fire when preparing the file data
30603 * @param {Roo.bootstrap.UploadCropbox} this
30604 * @param {Object} file
30609 * Fire when get exception
30610 * @param {Roo.bootstrap.UploadCropbox} this
30611 * @param {XMLHttpRequest} xhr
30613 "exception" : true,
30615 * @event beforeloadcanvas
30616 * Fire before load the canvas
30617 * @param {Roo.bootstrap.UploadCropbox} this
30618 * @param {String} src
30620 "beforeloadcanvas" : true,
30623 * Fire when trash image
30624 * @param {Roo.bootstrap.UploadCropbox} this
30629 * Fire when download the image
30630 * @param {Roo.bootstrap.UploadCropbox} this
30634 * @event footerbuttonclick
30635 * Fire when footerbuttonclick
30636 * @param {Roo.bootstrap.UploadCropbox} this
30637 * @param {String} type
30639 "footerbuttonclick" : true,
30643 * @param {Roo.bootstrap.UploadCropbox} this
30648 * Fire when rotate the image
30649 * @param {Roo.bootstrap.UploadCropbox} this
30650 * @param {String} pos
30655 * Fire when inspect the file
30656 * @param {Roo.bootstrap.UploadCropbox} this
30657 * @param {Object} file
30662 * Fire when xhr upload the file
30663 * @param {Roo.bootstrap.UploadCropbox} this
30664 * @param {Object} data
30669 * Fire when arrange the file data
30670 * @param {Roo.bootstrap.UploadCropbox} this
30671 * @param {Object} formData
30676 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30679 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30681 emptyText : 'Click to upload image',
30682 rotateNotify : 'Image is too small to rotate',
30683 errorTimeout : 3000,
30697 cropType : 'image/jpeg',
30699 canvasLoaded : false,
30700 isDocument : false,
30702 paramName : 'imageUpload',
30704 loadingText : 'Loading...',
30707 getAutoCreate : function()
30711 cls : 'roo-upload-cropbox',
30715 cls : 'roo-upload-cropbox-selector',
30720 cls : 'roo-upload-cropbox-body',
30721 style : 'cursor:pointer',
30725 cls : 'roo-upload-cropbox-preview'
30729 cls : 'roo-upload-cropbox-thumb'
30733 cls : 'roo-upload-cropbox-empty-notify',
30734 html : this.emptyText
30738 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30739 html : this.rotateNotify
30745 cls : 'roo-upload-cropbox-footer',
30748 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30758 onRender : function(ct, position)
30760 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30762 if (this.buttons.length) {
30764 Roo.each(this.buttons, function(bb) {
30766 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30768 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30774 this.maskEl = this.el;
30778 initEvents : function()
30780 this.urlAPI = (window.createObjectURL && window) ||
30781 (window.URL && URL.revokeObjectURL && URL) ||
30782 (window.webkitURL && webkitURL);
30784 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30785 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30787 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30788 this.selectorEl.hide();
30790 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30791 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30793 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30794 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30795 this.thumbEl.hide();
30797 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30798 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30800 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30801 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30802 this.errorEl.hide();
30804 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30805 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30806 this.footerEl.hide();
30808 this.setThumbBoxSize();
30814 this.fireEvent('initial', this);
30821 window.addEventListener("resize", function() { _this.resize(); } );
30823 this.bodyEl.on('click', this.beforeSelectFile, this);
30826 this.bodyEl.on('touchstart', this.onTouchStart, this);
30827 this.bodyEl.on('touchmove', this.onTouchMove, this);
30828 this.bodyEl.on('touchend', this.onTouchEnd, this);
30832 this.bodyEl.on('mousedown', this.onMouseDown, this);
30833 this.bodyEl.on('mousemove', this.onMouseMove, this);
30834 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30835 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30836 Roo.get(document).on('mouseup', this.onMouseUp, this);
30839 this.selectorEl.on('change', this.onFileSelected, this);
30845 this.baseScale = 1;
30847 this.baseRotate = 1;
30848 this.dragable = false;
30849 this.pinching = false;
30852 this.cropData = false;
30853 this.notifyEl.dom.innerHTML = this.emptyText;
30855 this.selectorEl.dom.value = '';
30859 resize : function()
30861 if(this.fireEvent('resize', this) != false){
30862 this.setThumbBoxPosition();
30863 this.setCanvasPosition();
30867 onFooterButtonClick : function(e, el, o, type)
30870 case 'rotate-left' :
30871 this.onRotateLeft(e);
30873 case 'rotate-right' :
30874 this.onRotateRight(e);
30877 this.beforeSelectFile(e);
30892 this.fireEvent('footerbuttonclick', this, type);
30895 beforeSelectFile : function(e)
30897 e.preventDefault();
30899 if(this.fireEvent('beforeselectfile', this) != false){
30900 this.selectorEl.dom.click();
30904 onFileSelected : function(e)
30906 e.preventDefault();
30908 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30912 var file = this.selectorEl.dom.files[0];
30914 if(this.fireEvent('inspect', this, file) != false){
30915 this.prepare(file);
30920 trash : function(e)
30922 this.fireEvent('trash', this);
30925 download : function(e)
30927 this.fireEvent('download', this);
30930 loadCanvas : function(src)
30932 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30936 this.imageEl = document.createElement('img');
30940 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30942 this.imageEl.src = src;
30946 onLoadCanvas : function()
30948 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30949 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30951 this.bodyEl.un('click', this.beforeSelectFile, this);
30953 this.notifyEl.hide();
30954 this.thumbEl.show();
30955 this.footerEl.show();
30957 this.baseRotateLevel();
30959 if(this.isDocument){
30960 this.setThumbBoxSize();
30963 this.setThumbBoxPosition();
30965 this.baseScaleLevel();
30971 this.canvasLoaded = true;
30974 this.maskEl.unmask();
30979 setCanvasPosition : function()
30981 if(!this.canvasEl){
30985 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30986 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30988 this.previewEl.setLeft(pw);
30989 this.previewEl.setTop(ph);
30993 onMouseDown : function(e)
30997 this.dragable = true;
30998 this.pinching = false;
31000 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31001 this.dragable = false;
31005 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31006 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31010 onMouseMove : function(e)
31014 if(!this.canvasLoaded){
31018 if (!this.dragable){
31022 var minX = Math.ceil(this.thumbEl.getLeft(true));
31023 var minY = Math.ceil(this.thumbEl.getTop(true));
31025 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31026 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31028 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31029 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31031 x = x - this.mouseX;
31032 y = y - this.mouseY;
31034 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31035 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31037 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31038 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31040 this.previewEl.setLeft(bgX);
31041 this.previewEl.setTop(bgY);
31043 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31044 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31047 onMouseUp : function(e)
31051 this.dragable = false;
31054 onMouseWheel : function(e)
31058 this.startScale = this.scale;
31060 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31062 if(!this.zoomable()){
31063 this.scale = this.startScale;
31072 zoomable : function()
31074 var minScale = this.thumbEl.getWidth() / this.minWidth;
31076 if(this.minWidth < this.minHeight){
31077 minScale = this.thumbEl.getHeight() / this.minHeight;
31080 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31081 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31085 (this.rotate == 0 || this.rotate == 180) &&
31087 width > this.imageEl.OriginWidth ||
31088 height > this.imageEl.OriginHeight ||
31089 (width < this.minWidth && height < this.minHeight)
31097 (this.rotate == 90 || this.rotate == 270) &&
31099 width > this.imageEl.OriginWidth ||
31100 height > this.imageEl.OriginHeight ||
31101 (width < this.minHeight && height < this.minWidth)
31108 !this.isDocument &&
31109 (this.rotate == 0 || this.rotate == 180) &&
31111 width < this.minWidth ||
31112 width > this.imageEl.OriginWidth ||
31113 height < this.minHeight ||
31114 height > this.imageEl.OriginHeight
31121 !this.isDocument &&
31122 (this.rotate == 90 || this.rotate == 270) &&
31124 width < this.minHeight ||
31125 width > this.imageEl.OriginWidth ||
31126 height < this.minWidth ||
31127 height > this.imageEl.OriginHeight
31137 onRotateLeft : function(e)
31139 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31141 var minScale = this.thumbEl.getWidth() / this.minWidth;
31143 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31144 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31146 this.startScale = this.scale;
31148 while (this.getScaleLevel() < minScale){
31150 this.scale = this.scale + 1;
31152 if(!this.zoomable()){
31157 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31158 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31163 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31170 this.scale = this.startScale;
31172 this.onRotateFail();
31177 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31179 if(this.isDocument){
31180 this.setThumbBoxSize();
31181 this.setThumbBoxPosition();
31182 this.setCanvasPosition();
31187 this.fireEvent('rotate', this, 'left');
31191 onRotateRight : function(e)
31193 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31195 var minScale = this.thumbEl.getWidth() / this.minWidth;
31197 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31198 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31200 this.startScale = this.scale;
31202 while (this.getScaleLevel() < minScale){
31204 this.scale = this.scale + 1;
31206 if(!this.zoomable()){
31211 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31212 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31217 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31224 this.scale = this.startScale;
31226 this.onRotateFail();
31231 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31233 if(this.isDocument){
31234 this.setThumbBoxSize();
31235 this.setThumbBoxPosition();
31236 this.setCanvasPosition();
31241 this.fireEvent('rotate', this, 'right');
31244 onRotateFail : function()
31246 this.errorEl.show(true);
31250 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31255 this.previewEl.dom.innerHTML = '';
31257 var canvasEl = document.createElement("canvas");
31259 var contextEl = canvasEl.getContext("2d");
31261 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31262 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31263 var center = this.imageEl.OriginWidth / 2;
31265 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31266 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31267 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31268 center = this.imageEl.OriginHeight / 2;
31271 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31273 contextEl.translate(center, center);
31274 contextEl.rotate(this.rotate * Math.PI / 180);
31276 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31278 this.canvasEl = document.createElement("canvas");
31280 this.contextEl = this.canvasEl.getContext("2d");
31282 switch (this.rotate) {
31285 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31286 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31288 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31293 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31294 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31296 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31297 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);
31301 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31306 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31307 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31309 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31310 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);
31314 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);
31319 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31320 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31322 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31323 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31327 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);
31334 this.previewEl.appendChild(this.canvasEl);
31336 this.setCanvasPosition();
31341 if(!this.canvasLoaded){
31345 var imageCanvas = document.createElement("canvas");
31347 var imageContext = imageCanvas.getContext("2d");
31349 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31350 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31352 var center = imageCanvas.width / 2;
31354 imageContext.translate(center, center);
31356 imageContext.rotate(this.rotate * Math.PI / 180);
31358 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31360 var canvas = document.createElement("canvas");
31362 var context = canvas.getContext("2d");
31364 canvas.width = this.minWidth;
31365 canvas.height = this.minHeight;
31367 switch (this.rotate) {
31370 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31371 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31373 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31374 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31376 var targetWidth = this.minWidth - 2 * x;
31377 var targetHeight = this.minHeight - 2 * y;
31381 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31382 scale = targetWidth / width;
31385 if(x > 0 && y == 0){
31386 scale = targetHeight / height;
31389 if(x > 0 && y > 0){
31390 scale = targetWidth / width;
31392 if(width < height){
31393 scale = targetHeight / height;
31397 context.scale(scale, scale);
31399 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31400 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31402 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31403 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31405 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31410 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31411 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31413 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31414 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31416 var targetWidth = this.minWidth - 2 * x;
31417 var targetHeight = this.minHeight - 2 * y;
31421 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31422 scale = targetWidth / width;
31425 if(x > 0 && y == 0){
31426 scale = targetHeight / height;
31429 if(x > 0 && y > 0){
31430 scale = targetWidth / width;
31432 if(width < height){
31433 scale = targetHeight / height;
31437 context.scale(scale, scale);
31439 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31440 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31442 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31443 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31445 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31447 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31452 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31453 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31455 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31456 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31458 var targetWidth = this.minWidth - 2 * x;
31459 var targetHeight = this.minHeight - 2 * y;
31463 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31464 scale = targetWidth / width;
31467 if(x > 0 && y == 0){
31468 scale = targetHeight / height;
31471 if(x > 0 && y > 0){
31472 scale = targetWidth / width;
31474 if(width < height){
31475 scale = targetHeight / height;
31479 context.scale(scale, scale);
31481 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31482 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31484 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31485 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31487 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31488 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31490 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31495 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31496 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31498 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31499 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31501 var targetWidth = this.minWidth - 2 * x;
31502 var targetHeight = this.minHeight - 2 * y;
31506 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31507 scale = targetWidth / width;
31510 if(x > 0 && y == 0){
31511 scale = targetHeight / height;
31514 if(x > 0 && y > 0){
31515 scale = targetWidth / width;
31517 if(width < height){
31518 scale = targetHeight / height;
31522 context.scale(scale, scale);
31524 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31525 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31527 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31528 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31530 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31532 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31539 this.cropData = canvas.toDataURL(this.cropType);
31541 if(this.fireEvent('crop', this, this.cropData) !== false){
31542 this.process(this.file, this.cropData);
31549 setThumbBoxSize : function()
31553 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31554 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31555 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31557 this.minWidth = width;
31558 this.minHeight = height;
31560 if(this.rotate == 90 || this.rotate == 270){
31561 this.minWidth = height;
31562 this.minHeight = width;
31567 width = Math.ceil(this.minWidth * height / this.minHeight);
31569 if(this.minWidth > this.minHeight){
31571 height = Math.ceil(this.minHeight * width / this.minWidth);
31574 this.thumbEl.setStyle({
31575 width : width + 'px',
31576 height : height + 'px'
31583 setThumbBoxPosition : function()
31585 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31586 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31588 this.thumbEl.setLeft(x);
31589 this.thumbEl.setTop(y);
31593 baseRotateLevel : function()
31595 this.baseRotate = 1;
31598 typeof(this.exif) != 'undefined' &&
31599 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31600 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31602 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31605 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31609 baseScaleLevel : function()
31613 if(this.isDocument){
31615 if(this.baseRotate == 6 || this.baseRotate == 8){
31617 height = this.thumbEl.getHeight();
31618 this.baseScale = height / this.imageEl.OriginWidth;
31620 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31621 width = this.thumbEl.getWidth();
31622 this.baseScale = width / this.imageEl.OriginHeight;
31628 height = this.thumbEl.getHeight();
31629 this.baseScale = height / this.imageEl.OriginHeight;
31631 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31632 width = this.thumbEl.getWidth();
31633 this.baseScale = width / this.imageEl.OriginWidth;
31639 if(this.baseRotate == 6 || this.baseRotate == 8){
31641 width = this.thumbEl.getHeight();
31642 this.baseScale = width / this.imageEl.OriginHeight;
31644 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31645 height = this.thumbEl.getWidth();
31646 this.baseScale = height / this.imageEl.OriginHeight;
31649 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31650 height = this.thumbEl.getWidth();
31651 this.baseScale = height / this.imageEl.OriginHeight;
31653 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31654 width = this.thumbEl.getHeight();
31655 this.baseScale = width / this.imageEl.OriginWidth;
31662 width = this.thumbEl.getWidth();
31663 this.baseScale = width / this.imageEl.OriginWidth;
31665 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31666 height = this.thumbEl.getHeight();
31667 this.baseScale = height / this.imageEl.OriginHeight;
31670 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31672 height = this.thumbEl.getHeight();
31673 this.baseScale = height / this.imageEl.OriginHeight;
31675 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31676 width = this.thumbEl.getWidth();
31677 this.baseScale = width / this.imageEl.OriginWidth;
31685 getScaleLevel : function()
31687 return this.baseScale * Math.pow(1.1, this.scale);
31690 onTouchStart : function(e)
31692 if(!this.canvasLoaded){
31693 this.beforeSelectFile(e);
31697 var touches = e.browserEvent.touches;
31703 if(touches.length == 1){
31704 this.onMouseDown(e);
31708 if(touches.length != 2){
31714 for(var i = 0, finger; finger = touches[i]; i++){
31715 coords.push(finger.pageX, finger.pageY);
31718 var x = Math.pow(coords[0] - coords[2], 2);
31719 var y = Math.pow(coords[1] - coords[3], 2);
31721 this.startDistance = Math.sqrt(x + y);
31723 this.startScale = this.scale;
31725 this.pinching = true;
31726 this.dragable = false;
31730 onTouchMove : function(e)
31732 if(!this.pinching && !this.dragable){
31736 var touches = e.browserEvent.touches;
31743 this.onMouseMove(e);
31749 for(var i = 0, finger; finger = touches[i]; i++){
31750 coords.push(finger.pageX, finger.pageY);
31753 var x = Math.pow(coords[0] - coords[2], 2);
31754 var y = Math.pow(coords[1] - coords[3], 2);
31756 this.endDistance = Math.sqrt(x + y);
31758 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31760 if(!this.zoomable()){
31761 this.scale = this.startScale;
31769 onTouchEnd : function(e)
31771 this.pinching = false;
31772 this.dragable = false;
31776 process : function(file, crop)
31779 this.maskEl.mask(this.loadingText);
31782 this.xhr = new XMLHttpRequest();
31784 file.xhr = this.xhr;
31786 this.xhr.open(this.method, this.url, true);
31789 "Accept": "application/json",
31790 "Cache-Control": "no-cache",
31791 "X-Requested-With": "XMLHttpRequest"
31794 for (var headerName in headers) {
31795 var headerValue = headers[headerName];
31797 this.xhr.setRequestHeader(headerName, headerValue);
31803 this.xhr.onload = function()
31805 _this.xhrOnLoad(_this.xhr);
31808 this.xhr.onerror = function()
31810 _this.xhrOnError(_this.xhr);
31813 var formData = new FormData();
31815 formData.append('returnHTML', 'NO');
31818 formData.append('crop', crop);
31821 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31822 formData.append(this.paramName, file, file.name);
31825 if(typeof(file.filename) != 'undefined'){
31826 formData.append('filename', file.filename);
31829 if(typeof(file.mimetype) != 'undefined'){
31830 formData.append('mimetype', file.mimetype);
31833 if(this.fireEvent('arrange', this, formData) != false){
31834 this.xhr.send(formData);
31838 xhrOnLoad : function(xhr)
31841 this.maskEl.unmask();
31844 if (xhr.readyState !== 4) {
31845 this.fireEvent('exception', this, xhr);
31849 var response = Roo.decode(xhr.responseText);
31851 if(!response.success){
31852 this.fireEvent('exception', this, xhr);
31856 var response = Roo.decode(xhr.responseText);
31858 this.fireEvent('upload', this, response);
31862 xhrOnError : function()
31865 this.maskEl.unmask();
31868 Roo.log('xhr on error');
31870 var response = Roo.decode(xhr.responseText);
31876 prepare : function(file)
31879 this.maskEl.mask(this.loadingText);
31885 if(typeof(file) === 'string'){
31886 this.loadCanvas(file);
31890 if(!file || !this.urlAPI){
31895 this.cropType = file.type;
31899 if(this.fireEvent('prepare', this, this.file) != false){
31901 var reader = new FileReader();
31903 reader.onload = function (e) {
31904 if (e.target.error) {
31905 Roo.log(e.target.error);
31909 var buffer = e.target.result,
31910 dataView = new DataView(buffer),
31912 maxOffset = dataView.byteLength - 4,
31916 if (dataView.getUint16(0) === 0xffd8) {
31917 while (offset < maxOffset) {
31918 markerBytes = dataView.getUint16(offset);
31920 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31921 markerLength = dataView.getUint16(offset + 2) + 2;
31922 if (offset + markerLength > dataView.byteLength) {
31923 Roo.log('Invalid meta data: Invalid segment size.');
31927 if(markerBytes == 0xffe1){
31928 _this.parseExifData(
31935 offset += markerLength;
31945 var url = _this.urlAPI.createObjectURL(_this.file);
31947 _this.loadCanvas(url);
31952 reader.readAsArrayBuffer(this.file);
31958 parseExifData : function(dataView, offset, length)
31960 var tiffOffset = offset + 10,
31964 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31965 // No Exif data, might be XMP data instead
31969 // Check for the ASCII code for "Exif" (0x45786966):
31970 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31971 // No Exif data, might be XMP data instead
31974 if (tiffOffset + 8 > dataView.byteLength) {
31975 Roo.log('Invalid Exif data: Invalid segment size.');
31978 // Check for the two null bytes:
31979 if (dataView.getUint16(offset + 8) !== 0x0000) {
31980 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31983 // Check the byte alignment:
31984 switch (dataView.getUint16(tiffOffset)) {
31986 littleEndian = true;
31989 littleEndian = false;
31992 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31995 // Check for the TIFF tag marker (0x002A):
31996 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31997 Roo.log('Invalid Exif data: Missing TIFF marker.');
32000 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32001 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32003 this.parseExifTags(
32006 tiffOffset + dirOffset,
32011 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32016 if (dirOffset + 6 > dataView.byteLength) {
32017 Roo.log('Invalid Exif data: Invalid directory offset.');
32020 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32021 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32022 if (dirEndOffset + 4 > dataView.byteLength) {
32023 Roo.log('Invalid Exif data: Invalid directory size.');
32026 for (i = 0; i < tagsNumber; i += 1) {
32030 dirOffset + 2 + 12 * i, // tag offset
32034 // Return the offset to the next directory:
32035 return dataView.getUint32(dirEndOffset, littleEndian);
32038 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32040 var tag = dataView.getUint16(offset, littleEndian);
32042 this.exif[tag] = this.getExifValue(
32046 dataView.getUint16(offset + 2, littleEndian), // tag type
32047 dataView.getUint32(offset + 4, littleEndian), // tag length
32052 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32054 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32063 Roo.log('Invalid Exif data: Invalid tag type.');
32067 tagSize = tagType.size * length;
32068 // Determine if the value is contained in the dataOffset bytes,
32069 // or if the value at the dataOffset is a pointer to the actual data:
32070 dataOffset = tagSize > 4 ?
32071 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32072 if (dataOffset + tagSize > dataView.byteLength) {
32073 Roo.log('Invalid Exif data: Invalid data offset.');
32076 if (length === 1) {
32077 return tagType.getValue(dataView, dataOffset, littleEndian);
32080 for (i = 0; i < length; i += 1) {
32081 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32084 if (tagType.ascii) {
32086 // Concatenate the chars:
32087 for (i = 0; i < values.length; i += 1) {
32089 // Ignore the terminating NULL byte(s):
32090 if (c === '\u0000') {
32102 Roo.apply(Roo.bootstrap.UploadCropbox, {
32104 'Orientation': 0x0112
32108 1: 0, //'top-left',
32110 3: 180, //'bottom-right',
32111 // 4: 'bottom-left',
32113 6: 90, //'right-top',
32114 // 7: 'right-bottom',
32115 8: 270 //'left-bottom'
32119 // byte, 8-bit unsigned int:
32121 getValue: function (dataView, dataOffset) {
32122 return dataView.getUint8(dataOffset);
32126 // ascii, 8-bit byte:
32128 getValue: function (dataView, dataOffset) {
32129 return String.fromCharCode(dataView.getUint8(dataOffset));
32134 // short, 16 bit int:
32136 getValue: function (dataView, dataOffset, littleEndian) {
32137 return dataView.getUint16(dataOffset, littleEndian);
32141 // long, 32 bit int:
32143 getValue: function (dataView, dataOffset, littleEndian) {
32144 return dataView.getUint32(dataOffset, littleEndian);
32148 // rational = two long values, first is numerator, second is denominator:
32150 getValue: function (dataView, dataOffset, littleEndian) {
32151 return dataView.getUint32(dataOffset, littleEndian) /
32152 dataView.getUint32(dataOffset + 4, littleEndian);
32156 // slong, 32 bit signed int:
32158 getValue: function (dataView, dataOffset, littleEndian) {
32159 return dataView.getInt32(dataOffset, littleEndian);
32163 // srational, two slongs, first is numerator, second is denominator:
32165 getValue: function (dataView, dataOffset, littleEndian) {
32166 return dataView.getInt32(dataOffset, littleEndian) /
32167 dataView.getInt32(dataOffset + 4, littleEndian);
32177 cls : 'btn-group roo-upload-cropbox-rotate-left',
32178 action : 'rotate-left',
32182 cls : 'btn btn-default',
32183 html : '<i class="fa fa-undo"></i>'
32189 cls : 'btn-group roo-upload-cropbox-picture',
32190 action : 'picture',
32194 cls : 'btn btn-default',
32195 html : '<i class="fa fa-picture-o"></i>'
32201 cls : 'btn-group roo-upload-cropbox-rotate-right',
32202 action : 'rotate-right',
32206 cls : 'btn btn-default',
32207 html : '<i class="fa fa-repeat"></i>'
32215 cls : 'btn-group roo-upload-cropbox-rotate-left',
32216 action : 'rotate-left',
32220 cls : 'btn btn-default',
32221 html : '<i class="fa fa-undo"></i>'
32227 cls : 'btn-group roo-upload-cropbox-download',
32228 action : 'download',
32232 cls : 'btn btn-default',
32233 html : '<i class="fa fa-download"></i>'
32239 cls : 'btn-group roo-upload-cropbox-crop',
32244 cls : 'btn btn-default',
32245 html : '<i class="fa fa-crop"></i>'
32251 cls : 'btn-group roo-upload-cropbox-trash',
32256 cls : 'btn btn-default',
32257 html : '<i class="fa fa-trash"></i>'
32263 cls : 'btn-group roo-upload-cropbox-rotate-right',
32264 action : 'rotate-right',
32268 cls : 'btn btn-default',
32269 html : '<i class="fa fa-repeat"></i>'
32277 cls : 'btn-group roo-upload-cropbox-rotate-left',
32278 action : 'rotate-left',
32282 cls : 'btn btn-default',
32283 html : '<i class="fa fa-undo"></i>'
32289 cls : 'btn-group roo-upload-cropbox-rotate-right',
32290 action : 'rotate-right',
32294 cls : 'btn btn-default',
32295 html : '<i class="fa fa-repeat"></i>'
32308 * @class Roo.bootstrap.DocumentManager
32309 * @extends Roo.bootstrap.Component
32310 * Bootstrap DocumentManager class
32311 * @cfg {String} paramName default 'imageUpload'
32312 * @cfg {String} toolTipName default 'filename'
32313 * @cfg {String} method default POST
32314 * @cfg {String} url action url
32315 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32316 * @cfg {Boolean} multiple multiple upload default true
32317 * @cfg {Number} thumbSize default 300
32318 * @cfg {String} fieldLabel
32319 * @cfg {Number} labelWidth default 4
32320 * @cfg {String} labelAlign (left|top) default left
32321 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32322 * @cfg {Number} labellg set the width of label (1-12)
32323 * @cfg {Number} labelmd set the width of label (1-12)
32324 * @cfg {Number} labelsm set the width of label (1-12)
32325 * @cfg {Number} labelxs set the width of label (1-12)
32328 * Create a new DocumentManager
32329 * @param {Object} config The config object
32332 Roo.bootstrap.DocumentManager = function(config){
32333 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32336 this.delegates = [];
32341 * Fire when initial the DocumentManager
32342 * @param {Roo.bootstrap.DocumentManager} this
32347 * inspect selected file
32348 * @param {Roo.bootstrap.DocumentManager} this
32349 * @param {File} file
32354 * Fire when xhr load exception
32355 * @param {Roo.bootstrap.DocumentManager} this
32356 * @param {XMLHttpRequest} xhr
32358 "exception" : true,
32360 * @event afterupload
32361 * Fire when xhr load exception
32362 * @param {Roo.bootstrap.DocumentManager} this
32363 * @param {XMLHttpRequest} xhr
32365 "afterupload" : true,
32368 * prepare the form data
32369 * @param {Roo.bootstrap.DocumentManager} this
32370 * @param {Object} formData
32375 * Fire when remove the file
32376 * @param {Roo.bootstrap.DocumentManager} this
32377 * @param {Object} file
32382 * Fire after refresh the file
32383 * @param {Roo.bootstrap.DocumentManager} this
32388 * Fire after click the image
32389 * @param {Roo.bootstrap.DocumentManager} this
32390 * @param {Object} file
32395 * Fire when upload a image and editable set to true
32396 * @param {Roo.bootstrap.DocumentManager} this
32397 * @param {Object} file
32401 * @event beforeselectfile
32402 * Fire before select file
32403 * @param {Roo.bootstrap.DocumentManager} this
32405 "beforeselectfile" : true,
32408 * Fire before process file
32409 * @param {Roo.bootstrap.DocumentManager} this
32410 * @param {Object} file
32414 * @event previewrendered
32415 * Fire when preview rendered
32416 * @param {Roo.bootstrap.DocumentManager} this
32417 * @param {Object} file
32419 "previewrendered" : true,
32422 "previewResize" : true
32427 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32436 paramName : 'imageUpload',
32437 toolTipName : 'filename',
32440 labelAlign : 'left',
32450 getAutoCreate : function()
32452 var managerWidget = {
32454 cls : 'roo-document-manager',
32458 cls : 'roo-document-manager-selector',
32463 cls : 'roo-document-manager-uploader',
32467 cls : 'roo-document-manager-upload-btn',
32468 html : '<i class="fa fa-plus"></i>'
32479 cls : 'column col-md-12',
32484 if(this.fieldLabel.length){
32489 cls : 'column col-md-12',
32490 html : this.fieldLabel
32494 cls : 'column col-md-12',
32499 if(this.labelAlign == 'left'){
32504 html : this.fieldLabel
32513 if(this.labelWidth > 12){
32514 content[0].style = "width: " + this.labelWidth + 'px';
32517 if(this.labelWidth < 13 && this.labelmd == 0){
32518 this.labelmd = this.labelWidth;
32521 if(this.labellg > 0){
32522 content[0].cls += ' col-lg-' + this.labellg;
32523 content[1].cls += ' col-lg-' + (12 - this.labellg);
32526 if(this.labelmd > 0){
32527 content[0].cls += ' col-md-' + this.labelmd;
32528 content[1].cls += ' col-md-' + (12 - this.labelmd);
32531 if(this.labelsm > 0){
32532 content[0].cls += ' col-sm-' + this.labelsm;
32533 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32536 if(this.labelxs > 0){
32537 content[0].cls += ' col-xs-' + this.labelxs;
32538 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32546 cls : 'row clearfix',
32554 initEvents : function()
32556 this.managerEl = this.el.select('.roo-document-manager', true).first();
32557 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32559 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32560 this.selectorEl.hide();
32563 this.selectorEl.attr('multiple', 'multiple');
32566 this.selectorEl.on('change', this.onFileSelected, this);
32568 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32569 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32571 this.uploader.on('click', this.onUploaderClick, this);
32573 this.renderProgressDialog();
32577 window.addEventListener("resize", function() { _this.refresh(); } );
32579 this.fireEvent('initial', this);
32582 renderProgressDialog : function()
32586 this.progressDialog = new Roo.bootstrap.Modal({
32587 cls : 'roo-document-manager-progress-dialog',
32588 allow_close : false,
32599 btnclick : function() {
32600 _this.uploadCancel();
32606 this.progressDialog.render(Roo.get(document.body));
32608 this.progress = new Roo.bootstrap.Progress({
32609 cls : 'roo-document-manager-progress',
32614 this.progress.render(this.progressDialog.getChildContainer());
32616 this.progressBar = new Roo.bootstrap.ProgressBar({
32617 cls : 'roo-document-manager-progress-bar',
32620 aria_valuemax : 12,
32624 this.progressBar.render(this.progress.getChildContainer());
32627 onUploaderClick : function(e)
32629 e.preventDefault();
32631 if(this.fireEvent('beforeselectfile', this) != false){
32632 this.selectorEl.dom.click();
32637 onFileSelected : function(e)
32639 e.preventDefault();
32641 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32645 Roo.each(this.selectorEl.dom.files, function(file){
32646 if(this.fireEvent('inspect', this, file) != false){
32647 this.files.push(file);
32657 this.selectorEl.dom.value = '';
32659 if(!this.files || !this.files.length){
32663 if(this.boxes > 0 && this.files.length > this.boxes){
32664 this.files = this.files.slice(0, this.boxes);
32667 this.uploader.show();
32669 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32670 this.uploader.hide();
32679 Roo.each(this.files, function(file){
32681 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32682 var f = this.renderPreview(file);
32687 if(file.type.indexOf('image') != -1){
32688 this.delegates.push(
32690 _this.process(file);
32691 }).createDelegate(this)
32699 _this.process(file);
32700 }).createDelegate(this)
32705 this.files = files;
32707 this.delegates = this.delegates.concat(docs);
32709 if(!this.delegates.length){
32714 this.progressBar.aria_valuemax = this.delegates.length;
32721 arrange : function()
32723 if(!this.delegates.length){
32724 this.progressDialog.hide();
32729 var delegate = this.delegates.shift();
32731 this.progressDialog.show();
32733 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32735 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32740 refresh : function()
32742 this.uploader.show();
32744 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32745 this.uploader.hide();
32748 Roo.isTouch ? this.closable(false) : this.closable(true);
32750 this.fireEvent('refresh', this);
32753 onRemove : function(e, el, o)
32755 e.preventDefault();
32757 this.fireEvent('remove', this, o);
32761 remove : function(o)
32765 Roo.each(this.files, function(file){
32766 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32775 this.files = files;
32782 Roo.each(this.files, function(file){
32787 file.target.remove();
32796 onClick : function(e, el, o)
32798 e.preventDefault();
32800 this.fireEvent('click', this, o);
32804 closable : function(closable)
32806 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32808 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32820 xhrOnLoad : function(xhr)
32822 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32826 if (xhr.readyState !== 4) {
32828 this.fireEvent('exception', this, xhr);
32832 var response = Roo.decode(xhr.responseText);
32834 if(!response.success){
32836 this.fireEvent('exception', this, xhr);
32840 var file = this.renderPreview(response.data);
32842 this.files.push(file);
32846 this.fireEvent('afterupload', this, xhr);
32850 xhrOnError : function(xhr)
32852 Roo.log('xhr on error');
32854 var response = Roo.decode(xhr.responseText);
32861 process : function(file)
32863 if(this.fireEvent('process', this, file) !== false){
32864 if(this.editable && file.type.indexOf('image') != -1){
32865 this.fireEvent('edit', this, file);
32869 this.uploadStart(file, false);
32876 uploadStart : function(file, crop)
32878 this.xhr = new XMLHttpRequest();
32880 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32885 file.xhr = this.xhr;
32887 this.managerEl.createChild({
32889 cls : 'roo-document-manager-loading',
32893 tooltip : file.name,
32894 cls : 'roo-document-manager-thumb',
32895 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32901 this.xhr.open(this.method, this.url, true);
32904 "Accept": "application/json",
32905 "Cache-Control": "no-cache",
32906 "X-Requested-With": "XMLHttpRequest"
32909 for (var headerName in headers) {
32910 var headerValue = headers[headerName];
32912 this.xhr.setRequestHeader(headerName, headerValue);
32918 this.xhr.onload = function()
32920 _this.xhrOnLoad(_this.xhr);
32923 this.xhr.onerror = function()
32925 _this.xhrOnError(_this.xhr);
32928 var formData = new FormData();
32930 formData.append('returnHTML', 'NO');
32933 formData.append('crop', crop);
32936 formData.append(this.paramName, file, file.name);
32943 if(this.fireEvent('prepare', this, formData, options) != false){
32945 if(options.manually){
32949 this.xhr.send(formData);
32953 this.uploadCancel();
32956 uploadCancel : function()
32962 this.delegates = [];
32964 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32971 renderPreview : function(file)
32973 if(typeof(file.target) != 'undefined' && file.target){
32977 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32979 var previewEl = this.managerEl.createChild({
32981 cls : 'roo-document-manager-preview',
32985 tooltip : file[this.toolTipName],
32986 cls : 'roo-document-manager-thumb',
32987 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32992 html : '<i class="fa fa-times-circle"></i>'
32997 var close = previewEl.select('button.close', true).first();
32999 close.on('click', this.onRemove, this, file);
33001 file.target = previewEl;
33003 var image = previewEl.select('img', true).first();
33007 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33009 image.on('click', this.onClick, this, file);
33011 this.fireEvent('previewrendered', this, file);
33017 onPreviewLoad : function(file, image)
33019 if(typeof(file.target) == 'undefined' || !file.target){
33023 var width = image.dom.naturalWidth || image.dom.width;
33024 var height = image.dom.naturalHeight || image.dom.height;
33026 if(!this.previewResize) {
33030 if(width > height){
33031 file.target.addClass('wide');
33035 file.target.addClass('tall');
33040 uploadFromSource : function(file, crop)
33042 this.xhr = new XMLHttpRequest();
33044 this.managerEl.createChild({
33046 cls : 'roo-document-manager-loading',
33050 tooltip : file.name,
33051 cls : 'roo-document-manager-thumb',
33052 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33058 this.xhr.open(this.method, this.url, true);
33061 "Accept": "application/json",
33062 "Cache-Control": "no-cache",
33063 "X-Requested-With": "XMLHttpRequest"
33066 for (var headerName in headers) {
33067 var headerValue = headers[headerName];
33069 this.xhr.setRequestHeader(headerName, headerValue);
33075 this.xhr.onload = function()
33077 _this.xhrOnLoad(_this.xhr);
33080 this.xhr.onerror = function()
33082 _this.xhrOnError(_this.xhr);
33085 var formData = new FormData();
33087 formData.append('returnHTML', 'NO');
33089 formData.append('crop', crop);
33091 if(typeof(file.filename) != 'undefined'){
33092 formData.append('filename', file.filename);
33095 if(typeof(file.mimetype) != 'undefined'){
33096 formData.append('mimetype', file.mimetype);
33101 if(this.fireEvent('prepare', this, formData) != false){
33102 this.xhr.send(formData);
33112 * @class Roo.bootstrap.DocumentViewer
33113 * @extends Roo.bootstrap.Component
33114 * Bootstrap DocumentViewer class
33115 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33116 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33119 * Create a new DocumentViewer
33120 * @param {Object} config The config object
33123 Roo.bootstrap.DocumentViewer = function(config){
33124 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33129 * Fire after initEvent
33130 * @param {Roo.bootstrap.DocumentViewer} this
33136 * @param {Roo.bootstrap.DocumentViewer} this
33141 * Fire after download button
33142 * @param {Roo.bootstrap.DocumentViewer} this
33147 * Fire after trash button
33148 * @param {Roo.bootstrap.DocumentViewer} this
33155 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33157 showDownload : true,
33161 getAutoCreate : function()
33165 cls : 'roo-document-viewer',
33169 cls : 'roo-document-viewer-body',
33173 cls : 'roo-document-viewer-thumb',
33177 cls : 'roo-document-viewer-image'
33185 cls : 'roo-document-viewer-footer',
33188 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33192 cls : 'btn-group roo-document-viewer-download',
33196 cls : 'btn btn-default',
33197 html : '<i class="fa fa-download"></i>'
33203 cls : 'btn-group roo-document-viewer-trash',
33207 cls : 'btn btn-default',
33208 html : '<i class="fa fa-trash"></i>'
33221 initEvents : function()
33223 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33224 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33226 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33227 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33229 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33230 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33232 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33233 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33235 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33236 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33238 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33239 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33241 this.bodyEl.on('click', this.onClick, this);
33242 this.downloadBtn.on('click', this.onDownload, this);
33243 this.trashBtn.on('click', this.onTrash, this);
33245 this.downloadBtn.hide();
33246 this.trashBtn.hide();
33248 if(this.showDownload){
33249 this.downloadBtn.show();
33252 if(this.showTrash){
33253 this.trashBtn.show();
33256 if(!this.showDownload && !this.showTrash) {
33257 this.footerEl.hide();
33262 initial : function()
33264 this.fireEvent('initial', this);
33268 onClick : function(e)
33270 e.preventDefault();
33272 this.fireEvent('click', this);
33275 onDownload : function(e)
33277 e.preventDefault();
33279 this.fireEvent('download', this);
33282 onTrash : function(e)
33284 e.preventDefault();
33286 this.fireEvent('trash', this);
33298 * @class Roo.bootstrap.NavProgressBar
33299 * @extends Roo.bootstrap.Component
33300 * Bootstrap NavProgressBar class
33303 * Create a new nav progress bar
33304 * @param {Object} config The config object
33307 Roo.bootstrap.NavProgressBar = function(config){
33308 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33310 this.bullets = this.bullets || [];
33312 // Roo.bootstrap.NavProgressBar.register(this);
33316 * Fires when the active item changes
33317 * @param {Roo.bootstrap.NavProgressBar} this
33318 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33319 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33326 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33331 getAutoCreate : function()
33333 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33337 cls : 'roo-navigation-bar-group',
33341 cls : 'roo-navigation-top-bar'
33345 cls : 'roo-navigation-bullets-bar',
33349 cls : 'roo-navigation-bar'
33356 cls : 'roo-navigation-bottom-bar'
33366 initEvents: function()
33371 onRender : function(ct, position)
33373 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33375 if(this.bullets.length){
33376 Roo.each(this.bullets, function(b){
33385 addItem : function(cfg)
33387 var item = new Roo.bootstrap.NavProgressItem(cfg);
33389 item.parentId = this.id;
33390 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33393 var top = new Roo.bootstrap.Element({
33395 cls : 'roo-navigation-bar-text'
33398 var bottom = new Roo.bootstrap.Element({
33400 cls : 'roo-navigation-bar-text'
33403 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33404 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33406 var topText = new Roo.bootstrap.Element({
33408 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33411 var bottomText = new Roo.bootstrap.Element({
33413 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33416 topText.onRender(top.el, null);
33417 bottomText.onRender(bottom.el, null);
33420 item.bottomEl = bottom;
33423 this.barItems.push(item);
33428 getActive : function()
33430 var active = false;
33432 Roo.each(this.barItems, function(v){
33434 if (!v.isActive()) {
33446 setActiveItem : function(item)
33450 Roo.each(this.barItems, function(v){
33451 if (v.rid == item.rid) {
33455 if (v.isActive()) {
33456 v.setActive(false);
33461 item.setActive(true);
33463 this.fireEvent('changed', this, item, prev);
33466 getBarItem: function(rid)
33470 Roo.each(this.barItems, function(e) {
33471 if (e.rid != rid) {
33482 indexOfItem : function(item)
33486 Roo.each(this.barItems, function(v, i){
33488 if (v.rid != item.rid) {
33499 setActiveNext : function()
33501 var i = this.indexOfItem(this.getActive());
33503 if (i > this.barItems.length) {
33507 this.setActiveItem(this.barItems[i+1]);
33510 setActivePrev : function()
33512 var i = this.indexOfItem(this.getActive());
33518 this.setActiveItem(this.barItems[i-1]);
33521 format : function()
33523 if(!this.barItems.length){
33527 var width = 100 / this.barItems.length;
33529 Roo.each(this.barItems, function(i){
33530 i.el.setStyle('width', width + '%');
33531 i.topEl.el.setStyle('width', width + '%');
33532 i.bottomEl.el.setStyle('width', width + '%');
33541 * Nav Progress Item
33546 * @class Roo.bootstrap.NavProgressItem
33547 * @extends Roo.bootstrap.Component
33548 * Bootstrap NavProgressItem class
33549 * @cfg {String} rid the reference id
33550 * @cfg {Boolean} active (true|false) Is item active default false
33551 * @cfg {Boolean} disabled (true|false) Is item active default false
33552 * @cfg {String} html
33553 * @cfg {String} position (top|bottom) text position default bottom
33554 * @cfg {String} icon show icon instead of number
33557 * Create a new NavProgressItem
33558 * @param {Object} config The config object
33560 Roo.bootstrap.NavProgressItem = function(config){
33561 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33566 * The raw click event for the entire grid.
33567 * @param {Roo.bootstrap.NavProgressItem} this
33568 * @param {Roo.EventObject} e
33575 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33581 position : 'bottom',
33584 getAutoCreate : function()
33586 var iconCls = 'roo-navigation-bar-item-icon';
33588 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33592 cls: 'roo-navigation-bar-item',
33602 cfg.cls += ' active';
33605 cfg.cls += ' disabled';
33611 disable : function()
33613 this.setDisabled(true);
33616 enable : function()
33618 this.setDisabled(false);
33621 initEvents: function()
33623 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33625 this.iconEl.on('click', this.onClick, this);
33628 onClick : function(e)
33630 e.preventDefault();
33636 if(this.fireEvent('click', this, e) === false){
33640 this.parent().setActiveItem(this);
33643 isActive: function ()
33645 return this.active;
33648 setActive : function(state)
33650 if(this.active == state){
33654 this.active = state;
33657 this.el.addClass('active');
33661 this.el.removeClass('active');
33666 setDisabled : function(state)
33668 if(this.disabled == state){
33672 this.disabled = state;
33675 this.el.addClass('disabled');
33679 this.el.removeClass('disabled');
33682 tooltipEl : function()
33684 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33697 * @class Roo.bootstrap.FieldLabel
33698 * @extends Roo.bootstrap.Component
33699 * Bootstrap FieldLabel class
33700 * @cfg {String} html contents of the element
33701 * @cfg {String} tag tag of the element default label
33702 * @cfg {String} cls class of the element
33703 * @cfg {String} target label target
33704 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33705 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33706 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33707 * @cfg {String} iconTooltip default "This field is required"
33708 * @cfg {String} indicatorpos (left|right) default left
33711 * Create a new FieldLabel
33712 * @param {Object} config The config object
33715 Roo.bootstrap.FieldLabel = function(config){
33716 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33721 * Fires after the field has been marked as invalid.
33722 * @param {Roo.form.FieldLabel} this
33723 * @param {String} msg The validation message
33728 * Fires after the field has been validated with no errors.
33729 * @param {Roo.form.FieldLabel} this
33735 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33742 invalidClass : 'has-warning',
33743 validClass : 'has-success',
33744 iconTooltip : 'This field is required',
33745 indicatorpos : 'left',
33747 getAutoCreate : function(){
33750 if (!this.allowBlank) {
33756 cls : 'roo-bootstrap-field-label ' + this.cls,
33761 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33762 tooltip : this.iconTooltip
33771 if(this.indicatorpos == 'right'){
33774 cls : 'roo-bootstrap-field-label ' + this.cls,
33783 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33784 tooltip : this.iconTooltip
33793 initEvents: function()
33795 Roo.bootstrap.Element.superclass.initEvents.call(this);
33797 this.indicator = this.indicatorEl();
33799 if(this.indicator){
33800 this.indicator.removeClass('visible');
33801 this.indicator.addClass('invisible');
33804 Roo.bootstrap.FieldLabel.register(this);
33807 indicatorEl : function()
33809 var indicator = this.el.select('i.roo-required-indicator',true).first();
33820 * Mark this field as valid
33822 markValid : function()
33824 if(this.indicator){
33825 this.indicator.removeClass('visible');
33826 this.indicator.addClass('invisible');
33828 if (Roo.bootstrap.version == 3) {
33829 this.el.removeClass(this.invalidClass);
33830 this.el.addClass(this.validClass);
33832 this.el.removeClass('is-invalid');
33833 this.el.addClass('is-valid');
33837 this.fireEvent('valid', this);
33841 * Mark this field as invalid
33842 * @param {String} msg The validation message
33844 markInvalid : function(msg)
33846 if(this.indicator){
33847 this.indicator.removeClass('invisible');
33848 this.indicator.addClass('visible');
33850 if (Roo.bootstrap.version == 3) {
33851 this.el.removeClass(this.validClass);
33852 this.el.addClass(this.invalidClass);
33854 this.el.removeClass('is-valid');
33855 this.el.addClass('is-invalid');
33859 this.fireEvent('invalid', this, msg);
33865 Roo.apply(Roo.bootstrap.FieldLabel, {
33870 * register a FieldLabel Group
33871 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33873 register : function(label)
33875 if(this.groups.hasOwnProperty(label.target)){
33879 this.groups[label.target] = label;
33883 * fetch a FieldLabel Group based on the target
33884 * @param {string} target
33885 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33887 get: function(target) {
33888 if (typeof(this.groups[target]) == 'undefined') {
33892 return this.groups[target] ;
33901 * page DateSplitField.
33907 * @class Roo.bootstrap.DateSplitField
33908 * @extends Roo.bootstrap.Component
33909 * Bootstrap DateSplitField class
33910 * @cfg {string} fieldLabel - the label associated
33911 * @cfg {Number} labelWidth set the width of label (0-12)
33912 * @cfg {String} labelAlign (top|left)
33913 * @cfg {Boolean} dayAllowBlank (true|false) default false
33914 * @cfg {Boolean} monthAllowBlank (true|false) default false
33915 * @cfg {Boolean} yearAllowBlank (true|false) default false
33916 * @cfg {string} dayPlaceholder
33917 * @cfg {string} monthPlaceholder
33918 * @cfg {string} yearPlaceholder
33919 * @cfg {string} dayFormat default 'd'
33920 * @cfg {string} monthFormat default 'm'
33921 * @cfg {string} yearFormat default 'Y'
33922 * @cfg {Number} labellg set the width of label (1-12)
33923 * @cfg {Number} labelmd set the width of label (1-12)
33924 * @cfg {Number} labelsm set the width of label (1-12)
33925 * @cfg {Number} labelxs set the width of label (1-12)
33929 * Create a new DateSplitField
33930 * @param {Object} config The config object
33933 Roo.bootstrap.DateSplitField = function(config){
33934 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33940 * getting the data of years
33941 * @param {Roo.bootstrap.DateSplitField} this
33942 * @param {Object} years
33947 * getting the data of days
33948 * @param {Roo.bootstrap.DateSplitField} this
33949 * @param {Object} days
33954 * Fires after the field has been marked as invalid.
33955 * @param {Roo.form.Field} this
33956 * @param {String} msg The validation message
33961 * Fires after the field has been validated with no errors.
33962 * @param {Roo.form.Field} this
33968 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33971 labelAlign : 'top',
33973 dayAllowBlank : false,
33974 monthAllowBlank : false,
33975 yearAllowBlank : false,
33976 dayPlaceholder : '',
33977 monthPlaceholder : '',
33978 yearPlaceholder : '',
33982 isFormField : true,
33988 getAutoCreate : function()
33992 cls : 'row roo-date-split-field-group',
33997 cls : 'form-hidden-field roo-date-split-field-group-value',
34003 var labelCls = 'col-md-12';
34004 var contentCls = 'col-md-4';
34006 if(this.fieldLabel){
34010 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34014 html : this.fieldLabel
34019 if(this.labelAlign == 'left'){
34021 if(this.labelWidth > 12){
34022 label.style = "width: " + this.labelWidth + 'px';
34025 if(this.labelWidth < 13 && this.labelmd == 0){
34026 this.labelmd = this.labelWidth;
34029 if(this.labellg > 0){
34030 labelCls = ' col-lg-' + this.labellg;
34031 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34034 if(this.labelmd > 0){
34035 labelCls = ' col-md-' + this.labelmd;
34036 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34039 if(this.labelsm > 0){
34040 labelCls = ' col-sm-' + this.labelsm;
34041 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34044 if(this.labelxs > 0){
34045 labelCls = ' col-xs-' + this.labelxs;
34046 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34050 label.cls += ' ' + labelCls;
34052 cfg.cn.push(label);
34055 Roo.each(['day', 'month', 'year'], function(t){
34058 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34065 inputEl: function ()
34067 return this.el.select('.roo-date-split-field-group-value', true).first();
34070 onRender : function(ct, position)
34074 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34076 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34078 this.dayField = new Roo.bootstrap.ComboBox({
34079 allowBlank : this.dayAllowBlank,
34080 alwaysQuery : true,
34081 displayField : 'value',
34084 forceSelection : true,
34086 placeholder : this.dayPlaceholder,
34087 selectOnFocus : true,
34088 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34089 triggerAction : 'all',
34091 valueField : 'value',
34092 store : new Roo.data.SimpleStore({
34093 data : (function() {
34095 _this.fireEvent('days', _this, days);
34098 fields : [ 'value' ]
34101 select : function (_self, record, index)
34103 _this.setValue(_this.getValue());
34108 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34110 this.monthField = new Roo.bootstrap.MonthField({
34111 after : '<i class=\"fa fa-calendar\"></i>',
34112 allowBlank : this.monthAllowBlank,
34113 placeholder : this.monthPlaceholder,
34116 render : function (_self)
34118 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34119 e.preventDefault();
34123 select : function (_self, oldvalue, newvalue)
34125 _this.setValue(_this.getValue());
34130 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34132 this.yearField = new Roo.bootstrap.ComboBox({
34133 allowBlank : this.yearAllowBlank,
34134 alwaysQuery : true,
34135 displayField : 'value',
34138 forceSelection : true,
34140 placeholder : this.yearPlaceholder,
34141 selectOnFocus : true,
34142 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34143 triggerAction : 'all',
34145 valueField : 'value',
34146 store : new Roo.data.SimpleStore({
34147 data : (function() {
34149 _this.fireEvent('years', _this, years);
34152 fields : [ 'value' ]
34155 select : function (_self, record, index)
34157 _this.setValue(_this.getValue());
34162 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34165 setValue : function(v, format)
34167 this.inputEl.dom.value = v;
34169 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34171 var d = Date.parseDate(v, f);
34178 this.setDay(d.format(this.dayFormat));
34179 this.setMonth(d.format(this.monthFormat));
34180 this.setYear(d.format(this.yearFormat));
34187 setDay : function(v)
34189 this.dayField.setValue(v);
34190 this.inputEl.dom.value = this.getValue();
34195 setMonth : function(v)
34197 this.monthField.setValue(v, true);
34198 this.inputEl.dom.value = this.getValue();
34203 setYear : function(v)
34205 this.yearField.setValue(v);
34206 this.inputEl.dom.value = this.getValue();
34211 getDay : function()
34213 return this.dayField.getValue();
34216 getMonth : function()
34218 return this.monthField.getValue();
34221 getYear : function()
34223 return this.yearField.getValue();
34226 getValue : function()
34228 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34230 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34240 this.inputEl.dom.value = '';
34245 validate : function()
34247 var d = this.dayField.validate();
34248 var m = this.monthField.validate();
34249 var y = this.yearField.validate();
34254 (!this.dayAllowBlank && !d) ||
34255 (!this.monthAllowBlank && !m) ||
34256 (!this.yearAllowBlank && !y)
34261 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34270 this.markInvalid();
34275 markValid : function()
34278 var label = this.el.select('label', true).first();
34279 var icon = this.el.select('i.fa-star', true).first();
34285 this.fireEvent('valid', this);
34289 * Mark this field as invalid
34290 * @param {String} msg The validation message
34292 markInvalid : function(msg)
34295 var label = this.el.select('label', true).first();
34296 var icon = this.el.select('i.fa-star', true).first();
34298 if(label && !icon){
34299 this.el.select('.roo-date-split-field-label', true).createChild({
34301 cls : 'text-danger fa fa-lg fa-star',
34302 tooltip : 'This field is required',
34303 style : 'margin-right:5px;'
34307 this.fireEvent('invalid', this, msg);
34310 clearInvalid : function()
34312 var label = this.el.select('label', true).first();
34313 var icon = this.el.select('i.fa-star', true).first();
34319 this.fireEvent('valid', this);
34322 getName: function()
34332 * http://masonry.desandro.com
34334 * The idea is to render all the bricks based on vertical width...
34336 * The original code extends 'outlayer' - we might need to use that....
34342 * @class Roo.bootstrap.LayoutMasonry
34343 * @extends Roo.bootstrap.Component
34344 * Bootstrap Layout Masonry class
34347 * Create a new Element
34348 * @param {Object} config The config object
34351 Roo.bootstrap.LayoutMasonry = function(config){
34353 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34357 Roo.bootstrap.LayoutMasonry.register(this);
34363 * Fire after layout the items
34364 * @param {Roo.bootstrap.LayoutMasonry} this
34365 * @param {Roo.EventObject} e
34372 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34375 * @cfg {Boolean} isLayoutInstant = no animation?
34377 isLayoutInstant : false, // needed?
34380 * @cfg {Number} boxWidth width of the columns
34385 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34390 * @cfg {Number} padWidth padding below box..
34395 * @cfg {Number} gutter gutter width..
34400 * @cfg {Number} maxCols maximum number of columns
34406 * @cfg {Boolean} isAutoInitial defalut true
34408 isAutoInitial : true,
34413 * @cfg {Boolean} isHorizontal defalut false
34415 isHorizontal : false,
34417 currentSize : null,
34423 bricks: null, //CompositeElement
34427 _isLayoutInited : false,
34429 // isAlternative : false, // only use for vertical layout...
34432 * @cfg {Number} alternativePadWidth padding below box..
34434 alternativePadWidth : 50,
34436 selectedBrick : [],
34438 getAutoCreate : function(){
34440 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34444 cls: 'blog-masonary-wrapper ' + this.cls,
34446 cls : 'mas-boxes masonary'
34453 getChildContainer: function( )
34455 if (this.boxesEl) {
34456 return this.boxesEl;
34459 this.boxesEl = this.el.select('.mas-boxes').first();
34461 return this.boxesEl;
34465 initEvents : function()
34469 if(this.isAutoInitial){
34470 Roo.log('hook children rendered');
34471 this.on('childrenrendered', function() {
34472 Roo.log('children rendered');
34478 initial : function()
34480 this.selectedBrick = [];
34482 this.currentSize = this.el.getBox(true);
34484 Roo.EventManager.onWindowResize(this.resize, this);
34486 if(!this.isAutoInitial){
34494 //this.layout.defer(500,this);
34498 resize : function()
34500 var cs = this.el.getBox(true);
34503 this.currentSize.width == cs.width &&
34504 this.currentSize.x == cs.x &&
34505 this.currentSize.height == cs.height &&
34506 this.currentSize.y == cs.y
34508 Roo.log("no change in with or X or Y");
34512 this.currentSize = cs;
34518 layout : function()
34520 this._resetLayout();
34522 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34524 this.layoutItems( isInstant );
34526 this._isLayoutInited = true;
34528 this.fireEvent('layout', this);
34532 _resetLayout : function()
34534 if(this.isHorizontal){
34535 this.horizontalMeasureColumns();
34539 this.verticalMeasureColumns();
34543 verticalMeasureColumns : function()
34545 this.getContainerWidth();
34547 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34548 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34552 var boxWidth = this.boxWidth + this.padWidth;
34554 if(this.containerWidth < this.boxWidth){
34555 boxWidth = this.containerWidth
34558 var containerWidth = this.containerWidth;
34560 var cols = Math.floor(containerWidth / boxWidth);
34562 this.cols = Math.max( cols, 1 );
34564 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34566 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34568 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34570 this.colWidth = boxWidth + avail - this.padWidth;
34572 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34573 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34576 horizontalMeasureColumns : function()
34578 this.getContainerWidth();
34580 var boxWidth = this.boxWidth;
34582 if(this.containerWidth < boxWidth){
34583 boxWidth = this.containerWidth;
34586 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34588 this.el.setHeight(boxWidth);
34592 getContainerWidth : function()
34594 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34597 layoutItems : function( isInstant )
34599 Roo.log(this.bricks);
34601 var items = Roo.apply([], this.bricks);
34603 if(this.isHorizontal){
34604 this._horizontalLayoutItems( items , isInstant );
34608 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34609 // this._verticalAlternativeLayoutItems( items , isInstant );
34613 this._verticalLayoutItems( items , isInstant );
34617 _verticalLayoutItems : function ( items , isInstant)
34619 if ( !items || !items.length ) {
34624 ['xs', 'xs', 'xs', 'tall'],
34625 ['xs', 'xs', 'tall'],
34626 ['xs', 'xs', 'sm'],
34627 ['xs', 'xs', 'xs'],
34633 ['sm', 'xs', 'xs'],
34637 ['tall', 'xs', 'xs', 'xs'],
34638 ['tall', 'xs', 'xs'],
34650 Roo.each(items, function(item, k){
34652 switch (item.size) {
34653 // these layouts take up a full box,
34664 boxes.push([item]);
34687 var filterPattern = function(box, length)
34695 var pattern = box.slice(0, length);
34699 Roo.each(pattern, function(i){
34700 format.push(i.size);
34703 Roo.each(standard, function(s){
34705 if(String(s) != String(format)){
34714 if(!match && length == 1){
34719 filterPattern(box, length - 1);
34723 queue.push(pattern);
34725 box = box.slice(length, box.length);
34727 filterPattern(box, 4);
34733 Roo.each(boxes, function(box, k){
34739 if(box.length == 1){
34744 filterPattern(box, 4);
34748 this._processVerticalLayoutQueue( queue, isInstant );
34752 // _verticalAlternativeLayoutItems : function( items , isInstant )
34754 // if ( !items || !items.length ) {
34758 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34762 _horizontalLayoutItems : function ( items , isInstant)
34764 if ( !items || !items.length || items.length < 3) {
34770 var eItems = items.slice(0, 3);
34772 items = items.slice(3, items.length);
34775 ['xs', 'xs', 'xs', 'wide'],
34776 ['xs', 'xs', 'wide'],
34777 ['xs', 'xs', 'sm'],
34778 ['xs', 'xs', 'xs'],
34784 ['sm', 'xs', 'xs'],
34788 ['wide', 'xs', 'xs', 'xs'],
34789 ['wide', 'xs', 'xs'],
34802 Roo.each(items, function(item, k){
34804 switch (item.size) {
34815 boxes.push([item]);
34839 var filterPattern = function(box, length)
34847 var pattern = box.slice(0, length);
34851 Roo.each(pattern, function(i){
34852 format.push(i.size);
34855 Roo.each(standard, function(s){
34857 if(String(s) != String(format)){
34866 if(!match && length == 1){
34871 filterPattern(box, length - 1);
34875 queue.push(pattern);
34877 box = box.slice(length, box.length);
34879 filterPattern(box, 4);
34885 Roo.each(boxes, function(box, k){
34891 if(box.length == 1){
34896 filterPattern(box, 4);
34903 var pos = this.el.getBox(true);
34907 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34909 var hit_end = false;
34911 Roo.each(queue, function(box){
34915 Roo.each(box, function(b){
34917 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34927 Roo.each(box, function(b){
34929 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34932 mx = Math.max(mx, b.x);
34936 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34940 Roo.each(box, function(b){
34942 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34956 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34959 /** Sets position of item in DOM
34960 * @param {Element} item
34961 * @param {Number} x - horizontal position
34962 * @param {Number} y - vertical position
34963 * @param {Boolean} isInstant - disables transitions
34965 _processVerticalLayoutQueue : function( queue, isInstant )
34967 var pos = this.el.getBox(true);
34972 for (var i = 0; i < this.cols; i++){
34976 Roo.each(queue, function(box, k){
34978 var col = k % this.cols;
34980 Roo.each(box, function(b,kk){
34982 b.el.position('absolute');
34984 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34985 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34987 if(b.size == 'md-left' || b.size == 'md-right'){
34988 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34989 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34992 b.el.setWidth(width);
34993 b.el.setHeight(height);
34995 b.el.select('iframe',true).setSize(width,height);
34999 for (var i = 0; i < this.cols; i++){
35001 if(maxY[i] < maxY[col]){
35006 col = Math.min(col, i);
35010 x = pos.x + col * (this.colWidth + this.padWidth);
35014 var positions = [];
35016 switch (box.length){
35018 positions = this.getVerticalOneBoxColPositions(x, y, box);
35021 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35024 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35027 positions = this.getVerticalFourBoxColPositions(x, y, box);
35033 Roo.each(box, function(b,kk){
35035 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35037 var sz = b.el.getSize();
35039 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35047 for (var i = 0; i < this.cols; i++){
35048 mY = Math.max(mY, maxY[i]);
35051 this.el.setHeight(mY - pos.y);
35055 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35057 // var pos = this.el.getBox(true);
35060 // var maxX = pos.right;
35062 // var maxHeight = 0;
35064 // Roo.each(items, function(item, k){
35068 // item.el.position('absolute');
35070 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35072 // item.el.setWidth(width);
35074 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35076 // item.el.setHeight(height);
35079 // item.el.setXY([x, y], isInstant ? false : true);
35081 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35084 // y = y + height + this.alternativePadWidth;
35086 // maxHeight = maxHeight + height + this.alternativePadWidth;
35090 // this.el.setHeight(maxHeight);
35094 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35096 var pos = this.el.getBox(true);
35101 var maxX = pos.right;
35103 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35105 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35107 Roo.each(queue, function(box, k){
35109 Roo.each(box, function(b, kk){
35111 b.el.position('absolute');
35113 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35114 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35116 if(b.size == 'md-left' || b.size == 'md-right'){
35117 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35118 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35121 b.el.setWidth(width);
35122 b.el.setHeight(height);
35130 var positions = [];
35132 switch (box.length){
35134 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35137 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35140 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35143 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35149 Roo.each(box, function(b,kk){
35151 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35153 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35161 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35163 Roo.each(eItems, function(b,k){
35165 b.size = (k == 0) ? 'sm' : 'xs';
35166 b.x = (k == 0) ? 2 : 1;
35167 b.y = (k == 0) ? 2 : 1;
35169 b.el.position('absolute');
35171 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35173 b.el.setWidth(width);
35175 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35177 b.el.setHeight(height);
35181 var positions = [];
35184 x : maxX - this.unitWidth * 2 - this.gutter,
35189 x : maxX - this.unitWidth,
35190 y : minY + (this.unitWidth + this.gutter) * 2
35194 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35198 Roo.each(eItems, function(b,k){
35200 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35206 getVerticalOneBoxColPositions : function(x, y, box)
35210 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35212 if(box[0].size == 'md-left'){
35216 if(box[0].size == 'md-right'){
35221 x : x + (this.unitWidth + this.gutter) * rand,
35228 getVerticalTwoBoxColPositions : function(x, y, box)
35232 if(box[0].size == 'xs'){
35236 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35240 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35254 x : x + (this.unitWidth + this.gutter) * 2,
35255 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35262 getVerticalThreeBoxColPositions : function(x, y, box)
35266 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35274 x : x + (this.unitWidth + this.gutter) * 1,
35279 x : x + (this.unitWidth + this.gutter) * 2,
35287 if(box[0].size == 'xs' && box[1].size == 'xs'){
35296 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35300 x : x + (this.unitWidth + this.gutter) * 1,
35314 x : x + (this.unitWidth + this.gutter) * 2,
35319 x : x + (this.unitWidth + this.gutter) * 2,
35320 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35327 getVerticalFourBoxColPositions : function(x, y, box)
35331 if(box[0].size == 'xs'){
35340 y : y + (this.unitHeight + this.gutter) * 1
35345 y : y + (this.unitHeight + this.gutter) * 2
35349 x : x + (this.unitWidth + this.gutter) * 1,
35363 x : x + (this.unitWidth + this.gutter) * 2,
35368 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35369 y : y + (this.unitHeight + this.gutter) * 1
35373 x : x + (this.unitWidth + this.gutter) * 2,
35374 y : y + (this.unitWidth + this.gutter) * 2
35381 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35385 if(box[0].size == 'md-left'){
35387 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35394 if(box[0].size == 'md-right'){
35396 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35397 y : minY + (this.unitWidth + this.gutter) * 1
35403 var rand = Math.floor(Math.random() * (4 - box[0].y));
35406 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35407 y : minY + (this.unitWidth + this.gutter) * rand
35414 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35418 if(box[0].size == 'xs'){
35421 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35426 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35427 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35435 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35440 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35441 y : minY + (this.unitWidth + this.gutter) * 2
35448 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35452 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35455 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35460 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35461 y : minY + (this.unitWidth + this.gutter) * 1
35465 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35466 y : minY + (this.unitWidth + this.gutter) * 2
35473 if(box[0].size == 'xs' && box[1].size == 'xs'){
35476 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35481 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35486 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35487 y : minY + (this.unitWidth + this.gutter) * 1
35495 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35500 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35501 y : minY + (this.unitWidth + this.gutter) * 2
35505 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35506 y : minY + (this.unitWidth + this.gutter) * 2
35513 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35517 if(box[0].size == 'xs'){
35520 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35525 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35530 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),
35535 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35536 y : minY + (this.unitWidth + this.gutter) * 1
35544 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35549 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35550 y : minY + (this.unitWidth + this.gutter) * 2
35554 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35555 y : minY + (this.unitWidth + this.gutter) * 2
35559 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),
35560 y : minY + (this.unitWidth + this.gutter) * 2
35568 * remove a Masonry Brick
35569 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35571 removeBrick : function(brick_id)
35577 for (var i = 0; i<this.bricks.length; i++) {
35578 if (this.bricks[i].id == brick_id) {
35579 this.bricks.splice(i,1);
35580 this.el.dom.removeChild(Roo.get(brick_id).dom);
35587 * adds a Masonry Brick
35588 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35590 addBrick : function(cfg)
35592 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35593 //this.register(cn);
35594 cn.parentId = this.id;
35595 cn.render(this.el);
35600 * register a Masonry Brick
35601 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35604 register : function(brick)
35606 this.bricks.push(brick);
35607 brick.masonryId = this.id;
35611 * clear all the Masonry Brick
35613 clearAll : function()
35616 //this.getChildContainer().dom.innerHTML = "";
35617 this.el.dom.innerHTML = '';
35620 getSelected : function()
35622 if (!this.selectedBrick) {
35626 return this.selectedBrick;
35630 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35634 * register a Masonry Layout
35635 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35638 register : function(layout)
35640 this.groups[layout.id] = layout;
35643 * fetch a Masonry Layout based on the masonry layout ID
35644 * @param {string} the masonry layout to add
35645 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35648 get: function(layout_id) {
35649 if (typeof(this.groups[layout_id]) == 'undefined') {
35652 return this.groups[layout_id] ;
35664 * http://masonry.desandro.com
35666 * The idea is to render all the bricks based on vertical width...
35668 * The original code extends 'outlayer' - we might need to use that....
35674 * @class Roo.bootstrap.LayoutMasonryAuto
35675 * @extends Roo.bootstrap.Component
35676 * Bootstrap Layout Masonry class
35679 * Create a new Element
35680 * @param {Object} config The config object
35683 Roo.bootstrap.LayoutMasonryAuto = function(config){
35684 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35687 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35690 * @cfg {Boolean} isFitWidth - resize the width..
35692 isFitWidth : false, // options..
35694 * @cfg {Boolean} isOriginLeft = left align?
35696 isOriginLeft : true,
35698 * @cfg {Boolean} isOriginTop = top align?
35700 isOriginTop : false,
35702 * @cfg {Boolean} isLayoutInstant = no animation?
35704 isLayoutInstant : false, // needed?
35706 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35708 isResizingContainer : true,
35710 * @cfg {Number} columnWidth width of the columns
35716 * @cfg {Number} maxCols maximum number of columns
35721 * @cfg {Number} padHeight padding below box..
35727 * @cfg {Boolean} isAutoInitial defalut true
35730 isAutoInitial : true,
35736 initialColumnWidth : 0,
35737 currentSize : null,
35739 colYs : null, // array.
35746 bricks: null, //CompositeElement
35747 cols : 0, // array?
35748 // element : null, // wrapped now this.el
35749 _isLayoutInited : null,
35752 getAutoCreate : function(){
35756 cls: 'blog-masonary-wrapper ' + this.cls,
35758 cls : 'mas-boxes masonary'
35765 getChildContainer: function( )
35767 if (this.boxesEl) {
35768 return this.boxesEl;
35771 this.boxesEl = this.el.select('.mas-boxes').first();
35773 return this.boxesEl;
35777 initEvents : function()
35781 if(this.isAutoInitial){
35782 Roo.log('hook children rendered');
35783 this.on('childrenrendered', function() {
35784 Roo.log('children rendered');
35791 initial : function()
35793 this.reloadItems();
35795 this.currentSize = this.el.getBox(true);
35797 /// was window resize... - let's see if this works..
35798 Roo.EventManager.onWindowResize(this.resize, this);
35800 if(!this.isAutoInitial){
35805 this.layout.defer(500,this);
35808 reloadItems: function()
35810 this.bricks = this.el.select('.masonry-brick', true);
35812 this.bricks.each(function(b) {
35813 //Roo.log(b.getSize());
35814 if (!b.attr('originalwidth')) {
35815 b.attr('originalwidth', b.getSize().width);
35820 Roo.log(this.bricks.elements.length);
35823 resize : function()
35826 var cs = this.el.getBox(true);
35828 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35829 Roo.log("no change in with or X");
35832 this.currentSize = cs;
35836 layout : function()
35839 this._resetLayout();
35840 //this._manageStamps();
35842 // don't animate first layout
35843 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35844 this.layoutItems( isInstant );
35846 // flag for initalized
35847 this._isLayoutInited = true;
35850 layoutItems : function( isInstant )
35852 //var items = this._getItemsForLayout( this.items );
35853 // original code supports filtering layout items.. we just ignore it..
35855 this._layoutItems( this.bricks , isInstant );
35857 this._postLayout();
35859 _layoutItems : function ( items , isInstant)
35861 //this.fireEvent( 'layout', this, items );
35864 if ( !items || !items.elements.length ) {
35865 // no items, emit event with empty array
35870 items.each(function(item) {
35871 Roo.log("layout item");
35873 // get x/y object from method
35874 var position = this._getItemLayoutPosition( item );
35876 position.item = item;
35877 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35878 queue.push( position );
35881 this._processLayoutQueue( queue );
35883 /** Sets position of item in DOM
35884 * @param {Element} item
35885 * @param {Number} x - horizontal position
35886 * @param {Number} y - vertical position
35887 * @param {Boolean} isInstant - disables transitions
35889 _processLayoutQueue : function( queue )
35891 for ( var i=0, len = queue.length; i < len; i++ ) {
35892 var obj = queue[i];
35893 obj.item.position('absolute');
35894 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35900 * Any logic you want to do after each layout,
35901 * i.e. size the container
35903 _postLayout : function()
35905 this.resizeContainer();
35908 resizeContainer : function()
35910 if ( !this.isResizingContainer ) {
35913 var size = this._getContainerSize();
35915 this.el.setSize(size.width,size.height);
35916 this.boxesEl.setSize(size.width,size.height);
35922 _resetLayout : function()
35924 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35925 this.colWidth = this.el.getWidth();
35926 //this.gutter = this.el.getWidth();
35928 this.measureColumns();
35934 this.colYs.push( 0 );
35940 measureColumns : function()
35942 this.getContainerWidth();
35943 // if columnWidth is 0, default to outerWidth of first item
35944 if ( !this.columnWidth ) {
35945 var firstItem = this.bricks.first();
35946 Roo.log(firstItem);
35947 this.columnWidth = this.containerWidth;
35948 if (firstItem && firstItem.attr('originalwidth') ) {
35949 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35951 // columnWidth fall back to item of first element
35952 Roo.log("set column width?");
35953 this.initialColumnWidth = this.columnWidth ;
35955 // if first elem has no width, default to size of container
35960 if (this.initialColumnWidth) {
35961 this.columnWidth = this.initialColumnWidth;
35966 // column width is fixed at the top - however if container width get's smaller we should
35969 // this bit calcs how man columns..
35971 var columnWidth = this.columnWidth += this.gutter;
35973 // calculate columns
35974 var containerWidth = this.containerWidth + this.gutter;
35976 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35977 // fix rounding errors, typically with gutters
35978 var excess = columnWidth - containerWidth % columnWidth;
35981 // if overshoot is less than a pixel, round up, otherwise floor it
35982 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35983 cols = Math[ mathMethod ]( cols );
35984 this.cols = Math.max( cols, 1 );
35985 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35987 // padding positioning..
35988 var totalColWidth = this.cols * this.columnWidth;
35989 var padavail = this.containerWidth - totalColWidth;
35990 // so for 2 columns - we need 3 'pads'
35992 var padNeeded = (1+this.cols) * this.padWidth;
35994 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35996 this.columnWidth += padExtra
35997 //this.padWidth = Math.floor(padavail / ( this.cols));
35999 // adjust colum width so that padding is fixed??
36001 // we have 3 columns ... total = width * 3
36002 // we have X left over... that should be used by
36004 //if (this.expandC) {
36012 getContainerWidth : function()
36014 /* // container is parent if fit width
36015 var container = this.isFitWidth ? this.element.parentNode : this.element;
36016 // check that this.size and size are there
36017 // IE8 triggers resize on body size change, so they might not be
36019 var size = getSize( container ); //FIXME
36020 this.containerWidth = size && size.innerWidth; //FIXME
36023 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36027 _getItemLayoutPosition : function( item ) // what is item?
36029 // we resize the item to our columnWidth..
36031 item.setWidth(this.columnWidth);
36032 item.autoBoxAdjust = false;
36034 var sz = item.getSize();
36036 // how many columns does this brick span
36037 var remainder = this.containerWidth % this.columnWidth;
36039 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36040 // round if off by 1 pixel, otherwise use ceil
36041 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36042 colSpan = Math.min( colSpan, this.cols );
36044 // normally this should be '1' as we dont' currently allow multi width columns..
36046 var colGroup = this._getColGroup( colSpan );
36047 // get the minimum Y value from the columns
36048 var minimumY = Math.min.apply( Math, colGroup );
36049 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36051 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36053 // position the brick
36055 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36056 y: this.currentSize.y + minimumY + this.padHeight
36060 // apply setHeight to necessary columns
36061 var setHeight = minimumY + sz.height + this.padHeight;
36062 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36064 var setSpan = this.cols + 1 - colGroup.length;
36065 for ( var i = 0; i < setSpan; i++ ) {
36066 this.colYs[ shortColIndex + i ] = setHeight ;
36073 * @param {Number} colSpan - number of columns the element spans
36074 * @returns {Array} colGroup
36076 _getColGroup : function( colSpan )
36078 if ( colSpan < 2 ) {
36079 // if brick spans only one column, use all the column Ys
36084 // how many different places could this brick fit horizontally
36085 var groupCount = this.cols + 1 - colSpan;
36086 // for each group potential horizontal position
36087 for ( var i = 0; i < groupCount; i++ ) {
36088 // make an array of colY values for that one group
36089 var groupColYs = this.colYs.slice( i, i + colSpan );
36090 // and get the max value of the array
36091 colGroup[i] = Math.max.apply( Math, groupColYs );
36096 _manageStamp : function( stamp )
36098 var stampSize = stamp.getSize();
36099 var offset = stamp.getBox();
36100 // get the columns that this stamp affects
36101 var firstX = this.isOriginLeft ? offset.x : offset.right;
36102 var lastX = firstX + stampSize.width;
36103 var firstCol = Math.floor( firstX / this.columnWidth );
36104 firstCol = Math.max( 0, firstCol );
36106 var lastCol = Math.floor( lastX / this.columnWidth );
36107 // lastCol should not go over if multiple of columnWidth #425
36108 lastCol -= lastX % this.columnWidth ? 0 : 1;
36109 lastCol = Math.min( this.cols - 1, lastCol );
36111 // set colYs to bottom of the stamp
36112 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36115 for ( var i = firstCol; i <= lastCol; i++ ) {
36116 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36121 _getContainerSize : function()
36123 this.maxY = Math.max.apply( Math, this.colYs );
36128 if ( this.isFitWidth ) {
36129 size.width = this._getContainerFitWidth();
36135 _getContainerFitWidth : function()
36137 var unusedCols = 0;
36138 // count unused columns
36141 if ( this.colYs[i] !== 0 ) {
36146 // fit container to columns that have been used
36147 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36150 needsResizeLayout : function()
36152 var previousWidth = this.containerWidth;
36153 this.getContainerWidth();
36154 return previousWidth !== this.containerWidth;
36169 * @class Roo.bootstrap.MasonryBrick
36170 * @extends Roo.bootstrap.Component
36171 * Bootstrap MasonryBrick class
36174 * Create a new MasonryBrick
36175 * @param {Object} config The config object
36178 Roo.bootstrap.MasonryBrick = function(config){
36180 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36182 Roo.bootstrap.MasonryBrick.register(this);
36188 * When a MasonryBrick is clcik
36189 * @param {Roo.bootstrap.MasonryBrick} this
36190 * @param {Roo.EventObject} e
36196 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36199 * @cfg {String} title
36203 * @cfg {String} html
36207 * @cfg {String} bgimage
36211 * @cfg {String} videourl
36215 * @cfg {String} cls
36219 * @cfg {String} href
36223 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36228 * @cfg {String} placetitle (center|bottom)
36233 * @cfg {Boolean} isFitContainer defalut true
36235 isFitContainer : true,
36238 * @cfg {Boolean} preventDefault defalut false
36240 preventDefault : false,
36243 * @cfg {Boolean} inverse defalut false
36245 maskInverse : false,
36247 getAutoCreate : function()
36249 if(!this.isFitContainer){
36250 return this.getSplitAutoCreate();
36253 var cls = 'masonry-brick masonry-brick-full';
36255 if(this.href.length){
36256 cls += ' masonry-brick-link';
36259 if(this.bgimage.length){
36260 cls += ' masonry-brick-image';
36263 if(this.maskInverse){
36264 cls += ' mask-inverse';
36267 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36268 cls += ' enable-mask';
36272 cls += ' masonry-' + this.size + '-brick';
36275 if(this.placetitle.length){
36277 switch (this.placetitle) {
36279 cls += ' masonry-center-title';
36282 cls += ' masonry-bottom-title';
36289 if(!this.html.length && !this.bgimage.length){
36290 cls += ' masonry-center-title';
36293 if(!this.html.length && this.bgimage.length){
36294 cls += ' masonry-bottom-title';
36299 cls += ' ' + this.cls;
36303 tag: (this.href.length) ? 'a' : 'div',
36308 cls: 'masonry-brick-mask'
36312 cls: 'masonry-brick-paragraph',
36318 if(this.href.length){
36319 cfg.href = this.href;
36322 var cn = cfg.cn[1].cn;
36324 if(this.title.length){
36327 cls: 'masonry-brick-title',
36332 if(this.html.length){
36335 cls: 'masonry-brick-text',
36340 if (!this.title.length && !this.html.length) {
36341 cfg.cn[1].cls += ' hide';
36344 if(this.bgimage.length){
36347 cls: 'masonry-brick-image-view',
36352 if(this.videourl.length){
36353 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36354 // youtube support only?
36357 cls: 'masonry-brick-image-view',
36360 allowfullscreen : true
36368 getSplitAutoCreate : function()
36370 var cls = 'masonry-brick masonry-brick-split';
36372 if(this.href.length){
36373 cls += ' masonry-brick-link';
36376 if(this.bgimage.length){
36377 cls += ' masonry-brick-image';
36381 cls += ' masonry-' + this.size + '-brick';
36384 switch (this.placetitle) {
36386 cls += ' masonry-center-title';
36389 cls += ' masonry-bottom-title';
36392 if(!this.bgimage.length){
36393 cls += ' masonry-center-title';
36396 if(this.bgimage.length){
36397 cls += ' masonry-bottom-title';
36403 cls += ' ' + this.cls;
36407 tag: (this.href.length) ? 'a' : 'div',
36412 cls: 'masonry-brick-split-head',
36416 cls: 'masonry-brick-paragraph',
36423 cls: 'masonry-brick-split-body',
36429 if(this.href.length){
36430 cfg.href = this.href;
36433 if(this.title.length){
36434 cfg.cn[0].cn[0].cn.push({
36436 cls: 'masonry-brick-title',
36441 if(this.html.length){
36442 cfg.cn[1].cn.push({
36444 cls: 'masonry-brick-text',
36449 if(this.bgimage.length){
36450 cfg.cn[0].cn.push({
36452 cls: 'masonry-brick-image-view',
36457 if(this.videourl.length){
36458 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36459 // youtube support only?
36460 cfg.cn[0].cn.cn.push({
36462 cls: 'masonry-brick-image-view',
36465 allowfullscreen : true
36472 initEvents: function()
36474 switch (this.size) {
36507 this.el.on('touchstart', this.onTouchStart, this);
36508 this.el.on('touchmove', this.onTouchMove, this);
36509 this.el.on('touchend', this.onTouchEnd, this);
36510 this.el.on('contextmenu', this.onContextMenu, this);
36512 this.el.on('mouseenter' ,this.enter, this);
36513 this.el.on('mouseleave', this.leave, this);
36514 this.el.on('click', this.onClick, this);
36517 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36518 this.parent().bricks.push(this);
36523 onClick: function(e, el)
36525 var time = this.endTimer - this.startTimer;
36526 // Roo.log(e.preventDefault());
36529 e.preventDefault();
36534 if(!this.preventDefault){
36538 e.preventDefault();
36540 if (this.activeClass != '') {
36541 this.selectBrick();
36544 this.fireEvent('click', this, e);
36547 enter: function(e, el)
36549 e.preventDefault();
36551 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36555 if(this.bgimage.length && this.html.length){
36556 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36560 leave: function(e, el)
36562 e.preventDefault();
36564 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36568 if(this.bgimage.length && this.html.length){
36569 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36573 onTouchStart: function(e, el)
36575 // e.preventDefault();
36577 this.touchmoved = false;
36579 if(!this.isFitContainer){
36583 if(!this.bgimage.length || !this.html.length){
36587 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36589 this.timer = new Date().getTime();
36593 onTouchMove: function(e, el)
36595 this.touchmoved = true;
36598 onContextMenu : function(e,el)
36600 e.preventDefault();
36601 e.stopPropagation();
36605 onTouchEnd: function(e, el)
36607 // e.preventDefault();
36609 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36616 if(!this.bgimage.length || !this.html.length){
36618 if(this.href.length){
36619 window.location.href = this.href;
36625 if(!this.isFitContainer){
36629 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36631 window.location.href = this.href;
36634 //selection on single brick only
36635 selectBrick : function() {
36637 if (!this.parentId) {
36641 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36642 var index = m.selectedBrick.indexOf(this.id);
36645 m.selectedBrick.splice(index,1);
36646 this.el.removeClass(this.activeClass);
36650 for(var i = 0; i < m.selectedBrick.length; i++) {
36651 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36652 b.el.removeClass(b.activeClass);
36655 m.selectedBrick = [];
36657 m.selectedBrick.push(this.id);
36658 this.el.addClass(this.activeClass);
36662 isSelected : function(){
36663 return this.el.hasClass(this.activeClass);
36668 Roo.apply(Roo.bootstrap.MasonryBrick, {
36671 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36673 * register a Masonry Brick
36674 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36677 register : function(brick)
36679 //this.groups[brick.id] = brick;
36680 this.groups.add(brick.id, brick);
36683 * fetch a masonry brick based on the masonry brick ID
36684 * @param {string} the masonry brick to add
36685 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36688 get: function(brick_id)
36690 // if (typeof(this.groups[brick_id]) == 'undefined') {
36693 // return this.groups[brick_id] ;
36695 if(this.groups.key(brick_id)) {
36696 return this.groups.key(brick_id);
36714 * @class Roo.bootstrap.Brick
36715 * @extends Roo.bootstrap.Component
36716 * Bootstrap Brick class
36719 * Create a new Brick
36720 * @param {Object} config The config object
36723 Roo.bootstrap.Brick = function(config){
36724 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36730 * When a Brick is click
36731 * @param {Roo.bootstrap.Brick} this
36732 * @param {Roo.EventObject} e
36738 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36741 * @cfg {String} title
36745 * @cfg {String} html
36749 * @cfg {String} bgimage
36753 * @cfg {String} cls
36757 * @cfg {String} href
36761 * @cfg {String} video
36765 * @cfg {Boolean} square
36769 getAutoCreate : function()
36771 var cls = 'roo-brick';
36773 if(this.href.length){
36774 cls += ' roo-brick-link';
36777 if(this.bgimage.length){
36778 cls += ' roo-brick-image';
36781 if(!this.html.length && !this.bgimage.length){
36782 cls += ' roo-brick-center-title';
36785 if(!this.html.length && this.bgimage.length){
36786 cls += ' roo-brick-bottom-title';
36790 cls += ' ' + this.cls;
36794 tag: (this.href.length) ? 'a' : 'div',
36799 cls: 'roo-brick-paragraph',
36805 if(this.href.length){
36806 cfg.href = this.href;
36809 var cn = cfg.cn[0].cn;
36811 if(this.title.length){
36814 cls: 'roo-brick-title',
36819 if(this.html.length){
36822 cls: 'roo-brick-text',
36829 if(this.bgimage.length){
36832 cls: 'roo-brick-image-view',
36840 initEvents: function()
36842 if(this.title.length || this.html.length){
36843 this.el.on('mouseenter' ,this.enter, this);
36844 this.el.on('mouseleave', this.leave, this);
36847 Roo.EventManager.onWindowResize(this.resize, this);
36849 if(this.bgimage.length){
36850 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36851 this.imageEl.on('load', this.onImageLoad, this);
36858 onImageLoad : function()
36863 resize : function()
36865 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36867 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36869 if(this.bgimage.length){
36870 var image = this.el.select('.roo-brick-image-view', true).first();
36872 image.setWidth(paragraph.getWidth());
36875 image.setHeight(paragraph.getWidth());
36878 this.el.setHeight(image.getHeight());
36879 paragraph.setHeight(image.getHeight());
36885 enter: function(e, el)
36887 e.preventDefault();
36889 if(this.bgimage.length){
36890 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36891 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36895 leave: function(e, el)
36897 e.preventDefault();
36899 if(this.bgimage.length){
36900 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36901 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36916 * @class Roo.bootstrap.NumberField
36917 * @extends Roo.bootstrap.Input
36918 * Bootstrap NumberField class
36924 * Create a new NumberField
36925 * @param {Object} config The config object
36928 Roo.bootstrap.NumberField = function(config){
36929 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36932 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36935 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36937 allowDecimals : true,
36939 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36941 decimalSeparator : ".",
36943 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36945 decimalPrecision : 2,
36947 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36949 allowNegative : true,
36952 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36956 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36958 minValue : Number.NEGATIVE_INFINITY,
36960 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36962 maxValue : Number.MAX_VALUE,
36964 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36966 minText : "The minimum value for this field is {0}",
36968 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36970 maxText : "The maximum value for this field is {0}",
36972 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36973 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36975 nanText : "{0} is not a valid number",
36977 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36979 thousandsDelimiter : false,
36981 * @cfg {String} valueAlign alignment of value
36983 valueAlign : "left",
36985 getAutoCreate : function()
36987 var hiddenInput = {
36991 cls: 'hidden-number-input'
36995 hiddenInput.name = this.name;
37000 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37002 this.name = hiddenInput.name;
37004 if(cfg.cn.length > 0) {
37005 cfg.cn.push(hiddenInput);
37012 initEvents : function()
37014 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37016 var allowed = "0123456789";
37018 if(this.allowDecimals){
37019 allowed += this.decimalSeparator;
37022 if(this.allowNegative){
37026 if(this.thousandsDelimiter) {
37030 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37032 var keyPress = function(e){
37034 var k = e.getKey();
37036 var c = e.getCharCode();
37039 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37040 allowed.indexOf(String.fromCharCode(c)) === -1
37046 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37050 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37055 this.el.on("keypress", keyPress, this);
37058 validateValue : function(value)
37061 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37065 var num = this.parseValue(value);
37068 this.markInvalid(String.format(this.nanText, value));
37072 if(num < this.minValue){
37073 this.markInvalid(String.format(this.minText, this.minValue));
37077 if(num > this.maxValue){
37078 this.markInvalid(String.format(this.maxText, this.maxValue));
37085 getValue : function()
37087 var v = this.hiddenEl().getValue();
37089 return this.fixPrecision(this.parseValue(v));
37092 parseValue : function(value)
37094 if(this.thousandsDelimiter) {
37096 r = new RegExp(",", "g");
37097 value = value.replace(r, "");
37100 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37101 return isNaN(value) ? '' : value;
37104 fixPrecision : function(value)
37106 if(this.thousandsDelimiter) {
37108 r = new RegExp(",", "g");
37109 value = value.replace(r, "");
37112 var nan = isNaN(value);
37114 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37115 return nan ? '' : value;
37117 return parseFloat(value).toFixed(this.decimalPrecision);
37120 setValue : function(v)
37122 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37128 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37130 this.inputEl().dom.value = (v == '') ? '' :
37131 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37133 if(!this.allowZero && v === '0') {
37134 this.hiddenEl().dom.value = '';
37135 this.inputEl().dom.value = '';
37142 decimalPrecisionFcn : function(v)
37144 return Math.floor(v);
37147 beforeBlur : function()
37149 var v = this.parseValue(this.getRawValue());
37151 if(v || v === 0 || v === ''){
37156 hiddenEl : function()
37158 return this.el.select('input.hidden-number-input',true).first();
37170 * @class Roo.bootstrap.DocumentSlider
37171 * @extends Roo.bootstrap.Component
37172 * Bootstrap DocumentSlider class
37175 * Create a new DocumentViewer
37176 * @param {Object} config The config object
37179 Roo.bootstrap.DocumentSlider = function(config){
37180 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37187 * Fire after initEvent
37188 * @param {Roo.bootstrap.DocumentSlider} this
37193 * Fire after update
37194 * @param {Roo.bootstrap.DocumentSlider} this
37200 * @param {Roo.bootstrap.DocumentSlider} this
37206 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37212 getAutoCreate : function()
37216 cls : 'roo-document-slider',
37220 cls : 'roo-document-slider-header',
37224 cls : 'roo-document-slider-header-title'
37230 cls : 'roo-document-slider-body',
37234 cls : 'roo-document-slider-prev',
37238 cls : 'fa fa-chevron-left'
37244 cls : 'roo-document-slider-thumb',
37248 cls : 'roo-document-slider-image'
37254 cls : 'roo-document-slider-next',
37258 cls : 'fa fa-chevron-right'
37270 initEvents : function()
37272 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37273 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37275 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37276 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37278 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37279 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37281 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37282 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37284 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37285 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37287 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37288 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37290 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37291 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37293 this.thumbEl.on('click', this.onClick, this);
37295 this.prevIndicator.on('click', this.prev, this);
37297 this.nextIndicator.on('click', this.next, this);
37301 initial : function()
37303 if(this.files.length){
37304 this.indicator = 1;
37308 this.fireEvent('initial', this);
37311 update : function()
37313 this.imageEl.attr('src', this.files[this.indicator - 1]);
37315 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37317 this.prevIndicator.show();
37319 if(this.indicator == 1){
37320 this.prevIndicator.hide();
37323 this.nextIndicator.show();
37325 if(this.indicator == this.files.length){
37326 this.nextIndicator.hide();
37329 this.thumbEl.scrollTo('top');
37331 this.fireEvent('update', this);
37334 onClick : function(e)
37336 e.preventDefault();
37338 this.fireEvent('click', this);
37343 e.preventDefault();
37345 this.indicator = Math.max(1, this.indicator - 1);
37352 e.preventDefault();
37354 this.indicator = Math.min(this.files.length, this.indicator + 1);
37368 * @class Roo.bootstrap.RadioSet
37369 * @extends Roo.bootstrap.Input
37370 * Bootstrap RadioSet class
37371 * @cfg {String} indicatorpos (left|right) default left
37372 * @cfg {Boolean} inline (true|false) inline the element (default true)
37373 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37375 * Create a new RadioSet
37376 * @param {Object} config The config object
37379 Roo.bootstrap.RadioSet = function(config){
37381 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37385 Roo.bootstrap.RadioSet.register(this);
37390 * Fires when the element is checked or unchecked.
37391 * @param {Roo.bootstrap.RadioSet} this This radio
37392 * @param {Roo.bootstrap.Radio} item The checked item
37397 * Fires when the element is click.
37398 * @param {Roo.bootstrap.RadioSet} this This radio set
37399 * @param {Roo.bootstrap.Radio} item The checked item
37400 * @param {Roo.EventObject} e The event object
37407 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37415 indicatorpos : 'left',
37417 getAutoCreate : function()
37421 cls : 'roo-radio-set-label',
37425 html : this.fieldLabel
37429 if (Roo.bootstrap.version == 3) {
37432 if(this.indicatorpos == 'left'){
37435 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37436 tooltip : 'This field is required'
37441 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37442 tooltip : 'This field is required'
37448 cls : 'roo-radio-set-items'
37451 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37453 if (align === 'left' && this.fieldLabel.length) {
37456 cls : "roo-radio-set-right",
37462 if(this.labelWidth > 12){
37463 label.style = "width: " + this.labelWidth + 'px';
37466 if(this.labelWidth < 13 && this.labelmd == 0){
37467 this.labelmd = this.labelWidth;
37470 if(this.labellg > 0){
37471 label.cls += ' col-lg-' + this.labellg;
37472 items.cls += ' col-lg-' + (12 - this.labellg);
37475 if(this.labelmd > 0){
37476 label.cls += ' col-md-' + this.labelmd;
37477 items.cls += ' col-md-' + (12 - this.labelmd);
37480 if(this.labelsm > 0){
37481 label.cls += ' col-sm-' + this.labelsm;
37482 items.cls += ' col-sm-' + (12 - this.labelsm);
37485 if(this.labelxs > 0){
37486 label.cls += ' col-xs-' + this.labelxs;
37487 items.cls += ' col-xs-' + (12 - this.labelxs);
37493 cls : 'roo-radio-set',
37497 cls : 'roo-radio-set-input',
37500 value : this.value ? this.value : ''
37507 if(this.weight.length){
37508 cfg.cls += ' roo-radio-' + this.weight;
37512 cfg.cls += ' roo-radio-set-inline';
37516 ['xs','sm','md','lg'].map(function(size){
37517 if (settings[size]) {
37518 cfg.cls += ' col-' + size + '-' + settings[size];
37526 initEvents : function()
37528 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37529 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37531 if(!this.fieldLabel.length){
37532 this.labelEl.hide();
37535 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37536 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37538 this.indicator = this.indicatorEl();
37540 if(this.indicator){
37541 this.indicator.addClass('invisible');
37544 this.originalValue = this.getValue();
37548 inputEl: function ()
37550 return this.el.select('.roo-radio-set-input', true).first();
37553 getChildContainer : function()
37555 return this.itemsEl;
37558 register : function(item)
37560 this.radioes.push(item);
37564 validate : function()
37566 if(this.getVisibilityEl().hasClass('hidden')){
37572 Roo.each(this.radioes, function(i){
37581 if(this.allowBlank) {
37585 if(this.disabled || valid){
37590 this.markInvalid();
37595 markValid : function()
37597 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37598 this.indicatorEl().removeClass('visible');
37599 this.indicatorEl().addClass('invisible');
37603 if (Roo.bootstrap.version == 3) {
37604 this.el.removeClass([this.invalidClass, this.validClass]);
37605 this.el.addClass(this.validClass);
37607 this.el.removeClass(['is-invalid','is-valid']);
37608 this.el.addClass(['is-valid']);
37610 this.fireEvent('valid', this);
37613 markInvalid : function(msg)
37615 if(this.allowBlank || this.disabled){
37619 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37620 this.indicatorEl().removeClass('invisible');
37621 this.indicatorEl().addClass('visible');
37623 if (Roo.bootstrap.version == 3) {
37624 this.el.removeClass([this.invalidClass, this.validClass]);
37625 this.el.addClass(this.invalidClass);
37627 this.el.removeClass(['is-invalid','is-valid']);
37628 this.el.addClass(['is-invalid']);
37631 this.fireEvent('invalid', this, msg);
37635 setValue : function(v, suppressEvent)
37637 if(this.value === v){
37644 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37647 Roo.each(this.radioes, function(i){
37649 i.el.removeClass('checked');
37652 Roo.each(this.radioes, function(i){
37654 if(i.value === v || i.value.toString() === v.toString()){
37656 i.el.addClass('checked');
37658 if(suppressEvent !== true){
37659 this.fireEvent('check', this, i);
37670 clearInvalid : function(){
37672 if(!this.el || this.preventMark){
37676 this.el.removeClass([this.invalidClass]);
37678 this.fireEvent('valid', this);
37683 Roo.apply(Roo.bootstrap.RadioSet, {
37687 register : function(set)
37689 this.groups[set.name] = set;
37692 get: function(name)
37694 if (typeof(this.groups[name]) == 'undefined') {
37698 return this.groups[name] ;
37704 * Ext JS Library 1.1.1
37705 * Copyright(c) 2006-2007, Ext JS, LLC.
37707 * Originally Released Under LGPL - original licence link has changed is not relivant.
37710 * <script type="text/javascript">
37715 * @class Roo.bootstrap.SplitBar
37716 * @extends Roo.util.Observable
37717 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37721 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37722 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37723 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37724 split.minSize = 100;
37725 split.maxSize = 600;
37726 split.animate = true;
37727 split.on('moved', splitterMoved);
37730 * Create a new SplitBar
37731 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37732 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37733 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37734 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37735 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37736 position of the SplitBar).
37738 Roo.bootstrap.SplitBar = function(cfg){
37743 // dragElement : elm
37744 // resizingElement: el,
37746 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37747 // placement : Roo.bootstrap.SplitBar.LEFT ,
37748 // existingProxy ???
37751 this.el = Roo.get(cfg.dragElement, true);
37752 this.el.dom.unselectable = "on";
37754 this.resizingEl = Roo.get(cfg.resizingElement, true);
37758 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37759 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37762 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37765 * The minimum size of the resizing element. (Defaults to 0)
37771 * The maximum size of the resizing element. (Defaults to 2000)
37774 this.maxSize = 2000;
37777 * Whether to animate the transition to the new size
37780 this.animate = false;
37783 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37786 this.useShim = false;
37791 if(!cfg.existingProxy){
37793 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37795 this.proxy = Roo.get(cfg.existingProxy).dom;
37798 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37801 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37804 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37807 this.dragSpecs = {};
37810 * @private The adapter to use to positon and resize elements
37812 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37813 this.adapter.init(this);
37815 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37817 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37818 this.el.addClass("roo-splitbar-h");
37821 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37822 this.el.addClass("roo-splitbar-v");
37828 * Fires when the splitter is moved (alias for {@link #event-moved})
37829 * @param {Roo.bootstrap.SplitBar} this
37830 * @param {Number} newSize the new width or height
37835 * Fires when the splitter is moved
37836 * @param {Roo.bootstrap.SplitBar} this
37837 * @param {Number} newSize the new width or height
37841 * @event beforeresize
37842 * Fires before the splitter is dragged
37843 * @param {Roo.bootstrap.SplitBar} this
37845 "beforeresize" : true,
37847 "beforeapply" : true
37850 Roo.util.Observable.call(this);
37853 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37854 onStartProxyDrag : function(x, y){
37855 this.fireEvent("beforeresize", this);
37857 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37859 o.enableDisplayMode("block");
37860 // all splitbars share the same overlay
37861 Roo.bootstrap.SplitBar.prototype.overlay = o;
37863 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37864 this.overlay.show();
37865 Roo.get(this.proxy).setDisplayed("block");
37866 var size = this.adapter.getElementSize(this);
37867 this.activeMinSize = this.getMinimumSize();;
37868 this.activeMaxSize = this.getMaximumSize();;
37869 var c1 = size - this.activeMinSize;
37870 var c2 = Math.max(this.activeMaxSize - size, 0);
37871 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37872 this.dd.resetConstraints();
37873 this.dd.setXConstraint(
37874 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37875 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37877 this.dd.setYConstraint(0, 0);
37879 this.dd.resetConstraints();
37880 this.dd.setXConstraint(0, 0);
37881 this.dd.setYConstraint(
37882 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37883 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37886 this.dragSpecs.startSize = size;
37887 this.dragSpecs.startPoint = [x, y];
37888 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37892 * @private Called after the drag operation by the DDProxy
37894 onEndProxyDrag : function(e){
37895 Roo.get(this.proxy).setDisplayed(false);
37896 var endPoint = Roo.lib.Event.getXY(e);
37898 this.overlay.hide();
37901 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37902 newSize = this.dragSpecs.startSize +
37903 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37904 endPoint[0] - this.dragSpecs.startPoint[0] :
37905 this.dragSpecs.startPoint[0] - endPoint[0]
37908 newSize = this.dragSpecs.startSize +
37909 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37910 endPoint[1] - this.dragSpecs.startPoint[1] :
37911 this.dragSpecs.startPoint[1] - endPoint[1]
37914 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37915 if(newSize != this.dragSpecs.startSize){
37916 if(this.fireEvent('beforeapply', this, newSize) !== false){
37917 this.adapter.setElementSize(this, newSize);
37918 this.fireEvent("moved", this, newSize);
37919 this.fireEvent("resize", this, newSize);
37925 * Get the adapter this SplitBar uses
37926 * @return The adapter object
37928 getAdapter : function(){
37929 return this.adapter;
37933 * Set the adapter this SplitBar uses
37934 * @param {Object} adapter A SplitBar adapter object
37936 setAdapter : function(adapter){
37937 this.adapter = adapter;
37938 this.adapter.init(this);
37942 * Gets the minimum size for the resizing element
37943 * @return {Number} The minimum size
37945 getMinimumSize : function(){
37946 return this.minSize;
37950 * Sets the minimum size for the resizing element
37951 * @param {Number} minSize The minimum size
37953 setMinimumSize : function(minSize){
37954 this.minSize = minSize;
37958 * Gets the maximum size for the resizing element
37959 * @return {Number} The maximum size
37961 getMaximumSize : function(){
37962 return this.maxSize;
37966 * Sets the maximum size for the resizing element
37967 * @param {Number} maxSize The maximum size
37969 setMaximumSize : function(maxSize){
37970 this.maxSize = maxSize;
37974 * Sets the initialize size for the resizing element
37975 * @param {Number} size The initial size
37977 setCurrentSize : function(size){
37978 var oldAnimate = this.animate;
37979 this.animate = false;
37980 this.adapter.setElementSize(this, size);
37981 this.animate = oldAnimate;
37985 * Destroy this splitbar.
37986 * @param {Boolean} removeEl True to remove the element
37988 destroy : function(removeEl){
37990 this.shim.remove();
37993 this.proxy.parentNode.removeChild(this.proxy);
38001 * @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.
38003 Roo.bootstrap.SplitBar.createProxy = function(dir){
38004 var proxy = new Roo.Element(document.createElement("div"));
38005 proxy.unselectable();
38006 var cls = 'roo-splitbar-proxy';
38007 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38008 document.body.appendChild(proxy.dom);
38013 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38014 * Default Adapter. It assumes the splitter and resizing element are not positioned
38015 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38017 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38020 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38021 // do nothing for now
38022 init : function(s){
38026 * Called before drag operations to get the current size of the resizing element.
38027 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38029 getElementSize : function(s){
38030 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38031 return s.resizingEl.getWidth();
38033 return s.resizingEl.getHeight();
38038 * Called after drag operations to set the size of the resizing element.
38039 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38040 * @param {Number} newSize The new size to set
38041 * @param {Function} onComplete A function to be invoked when resizing is complete
38043 setElementSize : function(s, newSize, onComplete){
38044 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38046 s.resizingEl.setWidth(newSize);
38048 onComplete(s, newSize);
38051 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38056 s.resizingEl.setHeight(newSize);
38058 onComplete(s, newSize);
38061 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38068 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38069 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38070 * Adapter that moves the splitter element to align with the resized sizing element.
38071 * Used with an absolute positioned SplitBar.
38072 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38073 * document.body, make sure you assign an id to the body element.
38075 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38076 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38077 this.container = Roo.get(container);
38080 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38081 init : function(s){
38082 this.basic.init(s);
38085 getElementSize : function(s){
38086 return this.basic.getElementSize(s);
38089 setElementSize : function(s, newSize, onComplete){
38090 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38093 moveSplitter : function(s){
38094 var yes = Roo.bootstrap.SplitBar;
38095 switch(s.placement){
38097 s.el.setX(s.resizingEl.getRight());
38100 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38103 s.el.setY(s.resizingEl.getBottom());
38106 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38113 * Orientation constant - Create a vertical SplitBar
38117 Roo.bootstrap.SplitBar.VERTICAL = 1;
38120 * Orientation constant - Create a horizontal SplitBar
38124 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38127 * Placement constant - The resizing element is to the left of the splitter element
38131 Roo.bootstrap.SplitBar.LEFT = 1;
38134 * Placement constant - The resizing element is to the right of the splitter element
38138 Roo.bootstrap.SplitBar.RIGHT = 2;
38141 * Placement constant - The resizing element is positioned above the splitter element
38145 Roo.bootstrap.SplitBar.TOP = 3;
38148 * Placement constant - The resizing element is positioned under splitter element
38152 Roo.bootstrap.SplitBar.BOTTOM = 4;
38153 Roo.namespace("Roo.bootstrap.layout");/*
38155 * Ext JS Library 1.1.1
38156 * Copyright(c) 2006-2007, Ext JS, LLC.
38158 * Originally Released Under LGPL - original licence link has changed is not relivant.
38161 * <script type="text/javascript">
38165 * @class Roo.bootstrap.layout.Manager
38166 * @extends Roo.bootstrap.Component
38167 * Base class for layout managers.
38169 Roo.bootstrap.layout.Manager = function(config)
38171 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38177 /** false to disable window resize monitoring @type Boolean */
38178 this.monitorWindowResize = true;
38183 * Fires when a layout is performed.
38184 * @param {Roo.LayoutManager} this
38188 * @event regionresized
38189 * Fires when the user resizes a region.
38190 * @param {Roo.LayoutRegion} region The resized region
38191 * @param {Number} newSize The new size (width for east/west, height for north/south)
38193 "regionresized" : true,
38195 * @event regioncollapsed
38196 * Fires when a region is collapsed.
38197 * @param {Roo.LayoutRegion} region The collapsed region
38199 "regioncollapsed" : true,
38201 * @event regionexpanded
38202 * Fires when a region is expanded.
38203 * @param {Roo.LayoutRegion} region The expanded region
38205 "regionexpanded" : true
38207 this.updating = false;
38210 this.el = Roo.get(config.el);
38216 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38221 monitorWindowResize : true,
38227 onRender : function(ct, position)
38230 this.el = Roo.get(ct);
38233 //this.fireEvent('render',this);
38237 initEvents: function()
38241 // ie scrollbar fix
38242 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38243 document.body.scroll = "no";
38244 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38245 this.el.position('relative');
38247 this.id = this.el.id;
38248 this.el.addClass("roo-layout-container");
38249 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38250 if(this.el.dom != document.body ) {
38251 this.el.on('resize', this.layout,this);
38252 this.el.on('show', this.layout,this);
38258 * Returns true if this layout is currently being updated
38259 * @return {Boolean}
38261 isUpdating : function(){
38262 return this.updating;
38266 * Suspend the LayoutManager from doing auto-layouts while
38267 * making multiple add or remove calls
38269 beginUpdate : function(){
38270 this.updating = true;
38274 * Restore auto-layouts and optionally disable the manager from performing a layout
38275 * @param {Boolean} noLayout true to disable a layout update
38277 endUpdate : function(noLayout){
38278 this.updating = false;
38284 layout: function(){
38288 onRegionResized : function(region, newSize){
38289 this.fireEvent("regionresized", region, newSize);
38293 onRegionCollapsed : function(region){
38294 this.fireEvent("regioncollapsed", region);
38297 onRegionExpanded : function(region){
38298 this.fireEvent("regionexpanded", region);
38302 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38303 * performs box-model adjustments.
38304 * @return {Object} The size as an object {width: (the width), height: (the height)}
38306 getViewSize : function()
38309 if(this.el.dom != document.body){
38310 size = this.el.getSize();
38312 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38314 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38315 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38320 * Returns the Element this layout is bound to.
38321 * @return {Roo.Element}
38323 getEl : function(){
38328 * Returns the specified region.
38329 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38330 * @return {Roo.LayoutRegion}
38332 getRegion : function(target){
38333 return this.regions[target.toLowerCase()];
38336 onWindowResize : function(){
38337 if(this.monitorWindowResize){
38344 * Ext JS Library 1.1.1
38345 * Copyright(c) 2006-2007, Ext JS, LLC.
38347 * Originally Released Under LGPL - original licence link has changed is not relivant.
38350 * <script type="text/javascript">
38353 * @class Roo.bootstrap.layout.Border
38354 * @extends Roo.bootstrap.layout.Manager
38355 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38356 * please see: examples/bootstrap/nested.html<br><br>
38358 <b>The container the layout is rendered into can be either the body element or any other element.
38359 If it is not the body element, the container needs to either be an absolute positioned element,
38360 or you will need to add "position:relative" to the css of the container. You will also need to specify
38361 the container size if it is not the body element.</b>
38364 * Create a new Border
38365 * @param {Object} config Configuration options
38367 Roo.bootstrap.layout.Border = function(config){
38368 config = config || {};
38369 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38373 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38374 if(config[region]){
38375 config[region].region = region;
38376 this.addRegion(config[region]);
38382 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38384 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38386 parent : false, // this might point to a 'nest' or a ???
38389 * Creates and adds a new region if it doesn't already exist.
38390 * @param {String} target The target region key (north, south, east, west or center).
38391 * @param {Object} config The regions config object
38392 * @return {BorderLayoutRegion} The new region
38394 addRegion : function(config)
38396 if(!this.regions[config.region]){
38397 var r = this.factory(config);
38398 this.bindRegion(r);
38400 return this.regions[config.region];
38404 bindRegion : function(r){
38405 this.regions[r.config.region] = r;
38407 r.on("visibilitychange", this.layout, this);
38408 r.on("paneladded", this.layout, this);
38409 r.on("panelremoved", this.layout, this);
38410 r.on("invalidated", this.layout, this);
38411 r.on("resized", this.onRegionResized, this);
38412 r.on("collapsed", this.onRegionCollapsed, this);
38413 r.on("expanded", this.onRegionExpanded, this);
38417 * Performs a layout update.
38419 layout : function()
38421 if(this.updating) {
38425 // render all the rebions if they have not been done alreayd?
38426 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38427 if(this.regions[region] && !this.regions[region].bodyEl){
38428 this.regions[region].onRender(this.el)
38432 var size = this.getViewSize();
38433 var w = size.width;
38434 var h = size.height;
38439 //var x = 0, y = 0;
38441 var rs = this.regions;
38442 var north = rs["north"];
38443 var south = rs["south"];
38444 var west = rs["west"];
38445 var east = rs["east"];
38446 var center = rs["center"];
38447 //if(this.hideOnLayout){ // not supported anymore
38448 //c.el.setStyle("display", "none");
38450 if(north && north.isVisible()){
38451 var b = north.getBox();
38452 var m = north.getMargins();
38453 b.width = w - (m.left+m.right);
38456 centerY = b.height + b.y + m.bottom;
38457 centerH -= centerY;
38458 north.updateBox(this.safeBox(b));
38460 if(south && south.isVisible()){
38461 var b = south.getBox();
38462 var m = south.getMargins();
38463 b.width = w - (m.left+m.right);
38465 var totalHeight = (b.height + m.top + m.bottom);
38466 b.y = h - totalHeight + m.top;
38467 centerH -= totalHeight;
38468 south.updateBox(this.safeBox(b));
38470 if(west && west.isVisible()){
38471 var b = west.getBox();
38472 var m = west.getMargins();
38473 b.height = centerH - (m.top+m.bottom);
38475 b.y = centerY + m.top;
38476 var totalWidth = (b.width + m.left + m.right);
38477 centerX += totalWidth;
38478 centerW -= totalWidth;
38479 west.updateBox(this.safeBox(b));
38481 if(east && east.isVisible()){
38482 var b = east.getBox();
38483 var m = east.getMargins();
38484 b.height = centerH - (m.top+m.bottom);
38485 var totalWidth = (b.width + m.left + m.right);
38486 b.x = w - totalWidth + m.left;
38487 b.y = centerY + m.top;
38488 centerW -= totalWidth;
38489 east.updateBox(this.safeBox(b));
38492 var m = center.getMargins();
38494 x: centerX + m.left,
38495 y: centerY + m.top,
38496 width: centerW - (m.left+m.right),
38497 height: centerH - (m.top+m.bottom)
38499 //if(this.hideOnLayout){
38500 //center.el.setStyle("display", "block");
38502 center.updateBox(this.safeBox(centerBox));
38505 this.fireEvent("layout", this);
38509 safeBox : function(box){
38510 box.width = Math.max(0, box.width);
38511 box.height = Math.max(0, box.height);
38516 * Adds a ContentPanel (or subclass) to this layout.
38517 * @param {String} target The target region key (north, south, east, west or center).
38518 * @param {Roo.ContentPanel} panel The panel to add
38519 * @return {Roo.ContentPanel} The added panel
38521 add : function(target, panel){
38523 target = target.toLowerCase();
38524 return this.regions[target].add(panel);
38528 * Remove a ContentPanel (or subclass) to this layout.
38529 * @param {String} target The target region key (north, south, east, west or center).
38530 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38531 * @return {Roo.ContentPanel} The removed panel
38533 remove : function(target, panel){
38534 target = target.toLowerCase();
38535 return this.regions[target].remove(panel);
38539 * Searches all regions for a panel with the specified id
38540 * @param {String} panelId
38541 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38543 findPanel : function(panelId){
38544 var rs = this.regions;
38545 for(var target in rs){
38546 if(typeof rs[target] != "function"){
38547 var p = rs[target].getPanel(panelId);
38557 * Searches all regions for a panel with the specified id and activates (shows) it.
38558 * @param {String/ContentPanel} panelId The panels id or the panel itself
38559 * @return {Roo.ContentPanel} The shown panel or null
38561 showPanel : function(panelId) {
38562 var rs = this.regions;
38563 for(var target in rs){
38564 var r = rs[target];
38565 if(typeof r != "function"){
38566 if(r.hasPanel(panelId)){
38567 return r.showPanel(panelId);
38575 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38576 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38579 restoreState : function(provider){
38581 provider = Roo.state.Manager;
38583 var sm = new Roo.LayoutStateManager();
38584 sm.init(this, provider);
38590 * Adds a xtype elements to the layout.
38594 xtype : 'ContentPanel',
38601 xtype : 'NestedLayoutPanel',
38607 items : [ ... list of content panels or nested layout panels.. ]
38611 * @param {Object} cfg Xtype definition of item to add.
38613 addxtype : function(cfg)
38615 // basically accepts a pannel...
38616 // can accept a layout region..!?!?
38617 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38620 // theory? children can only be panels??
38622 //if (!cfg.xtype.match(/Panel$/)) {
38627 if (typeof(cfg.region) == 'undefined') {
38628 Roo.log("Failed to add Panel, region was not set");
38632 var region = cfg.region;
38638 xitems = cfg.items;
38643 if ( region == 'center') {
38644 Roo.log("Center: " + cfg.title);
38650 case 'Content': // ContentPanel (el, cfg)
38651 case 'Scroll': // ContentPanel (el, cfg)
38653 cfg.autoCreate = cfg.autoCreate || true;
38654 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38656 // var el = this.el.createChild();
38657 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38660 this.add(region, ret);
38664 case 'TreePanel': // our new panel!
38665 cfg.el = this.el.createChild();
38666 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38667 this.add(region, ret);
38672 // create a new Layout (which is a Border Layout...
38674 var clayout = cfg.layout;
38675 clayout.el = this.el.createChild();
38676 clayout.items = clayout.items || [];
38680 // replace this exitems with the clayout ones..
38681 xitems = clayout.items;
38683 // force background off if it's in center...
38684 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38685 cfg.background = false;
38687 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38690 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38691 //console.log('adding nested layout panel ' + cfg.toSource());
38692 this.add(region, ret);
38693 nb = {}; /// find first...
38698 // needs grid and region
38700 //var el = this.getRegion(region).el.createChild();
38702 *var el = this.el.createChild();
38703 // create the grid first...
38704 cfg.grid.container = el;
38705 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38708 if (region == 'center' && this.active ) {
38709 cfg.background = false;
38712 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38714 this.add(region, ret);
38716 if (cfg.background) {
38717 // render grid on panel activation (if panel background)
38718 ret.on('activate', function(gp) {
38719 if (!gp.grid.rendered) {
38720 // gp.grid.render(el);
38724 // cfg.grid.render(el);
38730 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38731 // it was the old xcomponent building that caused this before.
38732 // espeically if border is the top element in the tree.
38742 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38744 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38745 this.add(region, ret);
38749 throw "Can not add '" + cfg.xtype + "' to Border";
38755 this.beginUpdate();
38759 Roo.each(xitems, function(i) {
38760 region = nb && i.region ? i.region : false;
38762 var add = ret.addxtype(i);
38765 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38766 if (!i.background) {
38767 abn[region] = nb[region] ;
38774 // make the last non-background panel active..
38775 //if (nb) { Roo.log(abn); }
38778 for(var r in abn) {
38779 region = this.getRegion(r);
38781 // tried using nb[r], but it does not work..
38783 region.showPanel(abn[r]);
38794 factory : function(cfg)
38797 var validRegions = Roo.bootstrap.layout.Border.regions;
38799 var target = cfg.region;
38802 var r = Roo.bootstrap.layout;
38806 return new r.North(cfg);
38808 return new r.South(cfg);
38810 return new r.East(cfg);
38812 return new r.West(cfg);
38814 return new r.Center(cfg);
38816 throw 'Layout region "'+target+'" not supported.';
38823 * Ext JS Library 1.1.1
38824 * Copyright(c) 2006-2007, Ext JS, LLC.
38826 * Originally Released Under LGPL - original licence link has changed is not relivant.
38829 * <script type="text/javascript">
38833 * @class Roo.bootstrap.layout.Basic
38834 * @extends Roo.util.Observable
38835 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38836 * and does not have a titlebar, tabs or any other features. All it does is size and position
38837 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38838 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38839 * @cfg {string} region the region that it inhabits..
38840 * @cfg {bool} skipConfig skip config?
38844 Roo.bootstrap.layout.Basic = function(config){
38846 this.mgr = config.mgr;
38848 this.position = config.region;
38850 var skipConfig = config.skipConfig;
38854 * @scope Roo.BasicLayoutRegion
38858 * @event beforeremove
38859 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38860 * @param {Roo.LayoutRegion} this
38861 * @param {Roo.ContentPanel} panel The panel
38862 * @param {Object} e The cancel event object
38864 "beforeremove" : true,
38866 * @event invalidated
38867 * Fires when the layout for this region is changed.
38868 * @param {Roo.LayoutRegion} this
38870 "invalidated" : true,
38872 * @event visibilitychange
38873 * Fires when this region is shown or hidden
38874 * @param {Roo.LayoutRegion} this
38875 * @param {Boolean} visibility true or false
38877 "visibilitychange" : true,
38879 * @event paneladded
38880 * Fires when a panel is added.
38881 * @param {Roo.LayoutRegion} this
38882 * @param {Roo.ContentPanel} panel The panel
38884 "paneladded" : true,
38886 * @event panelremoved
38887 * Fires when a panel is removed.
38888 * @param {Roo.LayoutRegion} this
38889 * @param {Roo.ContentPanel} panel The panel
38891 "panelremoved" : true,
38893 * @event beforecollapse
38894 * Fires when this region before collapse.
38895 * @param {Roo.LayoutRegion} this
38897 "beforecollapse" : true,
38900 * Fires when this region is collapsed.
38901 * @param {Roo.LayoutRegion} this
38903 "collapsed" : true,
38906 * Fires when this region is expanded.
38907 * @param {Roo.LayoutRegion} this
38912 * Fires when this region is slid into view.
38913 * @param {Roo.LayoutRegion} this
38915 "slideshow" : true,
38918 * Fires when this region slides out of view.
38919 * @param {Roo.LayoutRegion} this
38921 "slidehide" : true,
38923 * @event panelactivated
38924 * Fires when a panel is activated.
38925 * @param {Roo.LayoutRegion} this
38926 * @param {Roo.ContentPanel} panel The activated panel
38928 "panelactivated" : true,
38931 * Fires when the user resizes this region.
38932 * @param {Roo.LayoutRegion} this
38933 * @param {Number} newSize The new size (width for east/west, height for north/south)
38937 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38938 this.panels = new Roo.util.MixedCollection();
38939 this.panels.getKey = this.getPanelId.createDelegate(this);
38941 this.activePanel = null;
38942 // ensure listeners are added...
38944 if (config.listeners || config.events) {
38945 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38946 listeners : config.listeners || {},
38947 events : config.events || {}
38951 if(skipConfig !== true){
38952 this.applyConfig(config);
38956 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38958 getPanelId : function(p){
38962 applyConfig : function(config){
38963 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38964 this.config = config;
38969 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38970 * the width, for horizontal (north, south) the height.
38971 * @param {Number} newSize The new width or height
38973 resizeTo : function(newSize){
38974 var el = this.el ? this.el :
38975 (this.activePanel ? this.activePanel.getEl() : null);
38977 switch(this.position){
38980 el.setWidth(newSize);
38981 this.fireEvent("resized", this, newSize);
38985 el.setHeight(newSize);
38986 this.fireEvent("resized", this, newSize);
38992 getBox : function(){
38993 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38996 getMargins : function(){
38997 return this.margins;
39000 updateBox : function(box){
39002 var el = this.activePanel.getEl();
39003 el.dom.style.left = box.x + "px";
39004 el.dom.style.top = box.y + "px";
39005 this.activePanel.setSize(box.width, box.height);
39009 * Returns the container element for this region.
39010 * @return {Roo.Element}
39012 getEl : function(){
39013 return this.activePanel;
39017 * Returns true if this region is currently visible.
39018 * @return {Boolean}
39020 isVisible : function(){
39021 return this.activePanel ? true : false;
39024 setActivePanel : function(panel){
39025 panel = this.getPanel(panel);
39026 if(this.activePanel && this.activePanel != panel){
39027 this.activePanel.setActiveState(false);
39028 this.activePanel.getEl().setLeftTop(-10000,-10000);
39030 this.activePanel = panel;
39031 panel.setActiveState(true);
39033 panel.setSize(this.box.width, this.box.height);
39035 this.fireEvent("panelactivated", this, panel);
39036 this.fireEvent("invalidated");
39040 * Show the specified panel.
39041 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39042 * @return {Roo.ContentPanel} The shown panel or null
39044 showPanel : function(panel){
39045 panel = this.getPanel(panel);
39047 this.setActivePanel(panel);
39053 * Get the active panel for this region.
39054 * @return {Roo.ContentPanel} The active panel or null
39056 getActivePanel : function(){
39057 return this.activePanel;
39061 * Add the passed ContentPanel(s)
39062 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39063 * @return {Roo.ContentPanel} The panel added (if only one was added)
39065 add : function(panel){
39066 if(arguments.length > 1){
39067 for(var i = 0, len = arguments.length; i < len; i++) {
39068 this.add(arguments[i]);
39072 if(this.hasPanel(panel)){
39073 this.showPanel(panel);
39076 var el = panel.getEl();
39077 if(el.dom.parentNode != this.mgr.el.dom){
39078 this.mgr.el.dom.appendChild(el.dom);
39080 if(panel.setRegion){
39081 panel.setRegion(this);
39083 this.panels.add(panel);
39084 el.setStyle("position", "absolute");
39085 if(!panel.background){
39086 this.setActivePanel(panel);
39087 if(this.config.initialSize && this.panels.getCount()==1){
39088 this.resizeTo(this.config.initialSize);
39091 this.fireEvent("paneladded", this, panel);
39096 * Returns true if the panel is in this region.
39097 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39098 * @return {Boolean}
39100 hasPanel : function(panel){
39101 if(typeof panel == "object"){ // must be panel obj
39102 panel = panel.getId();
39104 return this.getPanel(panel) ? true : false;
39108 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39109 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39110 * @param {Boolean} preservePanel Overrides the config preservePanel option
39111 * @return {Roo.ContentPanel} The panel that was removed
39113 remove : function(panel, preservePanel){
39114 panel = this.getPanel(panel);
39119 this.fireEvent("beforeremove", this, panel, e);
39120 if(e.cancel === true){
39123 var panelId = panel.getId();
39124 this.panels.removeKey(panelId);
39129 * Returns the panel specified or null if it's not in this region.
39130 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39131 * @return {Roo.ContentPanel}
39133 getPanel : function(id){
39134 if(typeof id == "object"){ // must be panel obj
39137 return this.panels.get(id);
39141 * Returns this regions position (north/south/east/west/center).
39144 getPosition: function(){
39145 return this.position;
39149 * Ext JS Library 1.1.1
39150 * Copyright(c) 2006-2007, Ext JS, LLC.
39152 * Originally Released Under LGPL - original licence link has changed is not relivant.
39155 * <script type="text/javascript">
39159 * @class Roo.bootstrap.layout.Region
39160 * @extends Roo.bootstrap.layout.Basic
39161 * This class represents a region in a layout manager.
39163 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39164 * @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})
39165 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39166 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39167 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39168 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39169 * @cfg {String} title The title for the region (overrides panel titles)
39170 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39171 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39172 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39173 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39174 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39175 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39176 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39177 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39178 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39179 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39181 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39182 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39183 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39184 * @cfg {Number} width For East/West panels
39185 * @cfg {Number} height For North/South panels
39186 * @cfg {Boolean} split To show the splitter
39187 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39189 * @cfg {string} cls Extra CSS classes to add to region
39191 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39192 * @cfg {string} region the region that it inhabits..
39195 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39196 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39198 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39199 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39200 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39202 Roo.bootstrap.layout.Region = function(config)
39204 this.applyConfig(config);
39206 var mgr = config.mgr;
39207 var pos = config.region;
39208 config.skipConfig = true;
39209 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39212 this.onRender(mgr.el);
39215 this.visible = true;
39216 this.collapsed = false;
39217 this.unrendered_panels = [];
39220 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39222 position: '', // set by wrapper (eg. north/south etc..)
39223 unrendered_panels : null, // unrendered panels.
39225 tabPosition : false,
39227 mgr: false, // points to 'Border'
39230 createBody : function(){
39231 /** This region's body element
39232 * @type Roo.Element */
39233 this.bodyEl = this.el.createChild({
39235 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39239 onRender: function(ctr, pos)
39241 var dh = Roo.DomHelper;
39242 /** This region's container element
39243 * @type Roo.Element */
39244 this.el = dh.append(ctr.dom, {
39246 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39248 /** This region's title element
39249 * @type Roo.Element */
39251 this.titleEl = dh.append(this.el.dom, {
39253 unselectable: "on",
39254 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39256 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39257 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39261 this.titleEl.enableDisplayMode();
39262 /** This region's title text element
39263 * @type HTMLElement */
39264 this.titleTextEl = this.titleEl.dom.firstChild;
39265 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39267 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39268 this.closeBtn.enableDisplayMode();
39269 this.closeBtn.on("click", this.closeClicked, this);
39270 this.closeBtn.hide();
39272 this.createBody(this.config);
39273 if(this.config.hideWhenEmpty){
39275 this.on("paneladded", this.validateVisibility, this);
39276 this.on("panelremoved", this.validateVisibility, this);
39278 if(this.autoScroll){
39279 this.bodyEl.setStyle("overflow", "auto");
39281 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39283 //if(c.titlebar !== false){
39284 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39285 this.titleEl.hide();
39287 this.titleEl.show();
39288 if(this.config.title){
39289 this.titleTextEl.innerHTML = this.config.title;
39293 if(this.config.collapsed){
39294 this.collapse(true);
39296 if(this.config.hidden){
39300 if (this.unrendered_panels && this.unrendered_panels.length) {
39301 for (var i =0;i< this.unrendered_panels.length; i++) {
39302 this.add(this.unrendered_panels[i]);
39304 this.unrendered_panels = null;
39310 applyConfig : function(c)
39313 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39314 var dh = Roo.DomHelper;
39315 if(c.titlebar !== false){
39316 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39317 this.collapseBtn.on("click", this.collapse, this);
39318 this.collapseBtn.enableDisplayMode();
39320 if(c.showPin === true || this.showPin){
39321 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39322 this.stickBtn.enableDisplayMode();
39323 this.stickBtn.on("click", this.expand, this);
39324 this.stickBtn.hide();
39329 /** This region's collapsed element
39330 * @type Roo.Element */
39333 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39334 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39337 if(c.floatable !== false){
39338 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39339 this.collapsedEl.on("click", this.collapseClick, this);
39342 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39343 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39344 id: "message", unselectable: "on", style:{"float":"left"}});
39345 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39347 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39348 this.expandBtn.on("click", this.expand, this);
39352 if(this.collapseBtn){
39353 this.collapseBtn.setVisible(c.collapsible == true);
39356 this.cmargins = c.cmargins || this.cmargins ||
39357 (this.position == "west" || this.position == "east" ?
39358 {top: 0, left: 2, right:2, bottom: 0} :
39359 {top: 2, left: 0, right:0, bottom: 2});
39361 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39364 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39366 this.autoScroll = c.autoScroll || false;
39371 this.duration = c.duration || .30;
39372 this.slideDuration = c.slideDuration || .45;
39377 * Returns true if this region is currently visible.
39378 * @return {Boolean}
39380 isVisible : function(){
39381 return this.visible;
39385 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39386 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39388 //setCollapsedTitle : function(title){
39389 // title = title || " ";
39390 // if(this.collapsedTitleTextEl){
39391 // this.collapsedTitleTextEl.innerHTML = title;
39395 getBox : function(){
39397 // if(!this.collapsed){
39398 b = this.el.getBox(false, true);
39400 // b = this.collapsedEl.getBox(false, true);
39405 getMargins : function(){
39406 return this.margins;
39407 //return this.collapsed ? this.cmargins : this.margins;
39410 highlight : function(){
39411 this.el.addClass("x-layout-panel-dragover");
39414 unhighlight : function(){
39415 this.el.removeClass("x-layout-panel-dragover");
39418 updateBox : function(box)
39420 if (!this.bodyEl) {
39421 return; // not rendered yet..
39425 if(!this.collapsed){
39426 this.el.dom.style.left = box.x + "px";
39427 this.el.dom.style.top = box.y + "px";
39428 this.updateBody(box.width, box.height);
39430 this.collapsedEl.dom.style.left = box.x + "px";
39431 this.collapsedEl.dom.style.top = box.y + "px";
39432 this.collapsedEl.setSize(box.width, box.height);
39435 this.tabs.autoSizeTabs();
39439 updateBody : function(w, h)
39442 this.el.setWidth(w);
39443 w -= this.el.getBorderWidth("rl");
39444 if(this.config.adjustments){
39445 w += this.config.adjustments[0];
39448 if(h !== null && h > 0){
39449 this.el.setHeight(h);
39450 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39451 h -= this.el.getBorderWidth("tb");
39452 if(this.config.adjustments){
39453 h += this.config.adjustments[1];
39455 this.bodyEl.setHeight(h);
39457 h = this.tabs.syncHeight(h);
39460 if(this.panelSize){
39461 w = w !== null ? w : this.panelSize.width;
39462 h = h !== null ? h : this.panelSize.height;
39464 if(this.activePanel){
39465 var el = this.activePanel.getEl();
39466 w = w !== null ? w : el.getWidth();
39467 h = h !== null ? h : el.getHeight();
39468 this.panelSize = {width: w, height: h};
39469 this.activePanel.setSize(w, h);
39471 if(Roo.isIE && this.tabs){
39472 this.tabs.el.repaint();
39477 * Returns the container element for this region.
39478 * @return {Roo.Element}
39480 getEl : function(){
39485 * Hides this region.
39488 //if(!this.collapsed){
39489 this.el.dom.style.left = "-2000px";
39492 // this.collapsedEl.dom.style.left = "-2000px";
39493 // this.collapsedEl.hide();
39495 this.visible = false;
39496 this.fireEvent("visibilitychange", this, false);
39500 * Shows this region if it was previously hidden.
39503 //if(!this.collapsed){
39506 // this.collapsedEl.show();
39508 this.visible = true;
39509 this.fireEvent("visibilitychange", this, true);
39512 closeClicked : function(){
39513 if(this.activePanel){
39514 this.remove(this.activePanel);
39518 collapseClick : function(e){
39520 e.stopPropagation();
39523 e.stopPropagation();
39529 * Collapses this region.
39530 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39533 collapse : function(skipAnim, skipCheck = false){
39534 if(this.collapsed) {
39538 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39540 this.collapsed = true;
39542 this.split.el.hide();
39544 if(this.config.animate && skipAnim !== true){
39545 this.fireEvent("invalidated", this);
39546 this.animateCollapse();
39548 this.el.setLocation(-20000,-20000);
39550 this.collapsedEl.show();
39551 this.fireEvent("collapsed", this);
39552 this.fireEvent("invalidated", this);
39558 animateCollapse : function(){
39563 * Expands this region if it was previously collapsed.
39564 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39565 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39568 expand : function(e, skipAnim){
39570 e.stopPropagation();
39572 if(!this.collapsed || this.el.hasActiveFx()) {
39576 this.afterSlideIn();
39579 this.collapsed = false;
39580 if(this.config.animate && skipAnim !== true){
39581 this.animateExpand();
39585 this.split.el.show();
39587 this.collapsedEl.setLocation(-2000,-2000);
39588 this.collapsedEl.hide();
39589 this.fireEvent("invalidated", this);
39590 this.fireEvent("expanded", this);
39594 animateExpand : function(){
39598 initTabs : function()
39600 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39602 var ts = new Roo.bootstrap.panel.Tabs({
39603 el: this.bodyEl.dom,
39605 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39606 disableTooltips: this.config.disableTabTips,
39607 toolbar : this.config.toolbar
39610 if(this.config.hideTabs){
39611 ts.stripWrap.setDisplayed(false);
39614 ts.resizeTabs = this.config.resizeTabs === true;
39615 ts.minTabWidth = this.config.minTabWidth || 40;
39616 ts.maxTabWidth = this.config.maxTabWidth || 250;
39617 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39618 ts.monitorResize = false;
39619 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39620 ts.bodyEl.addClass('roo-layout-tabs-body');
39621 this.panels.each(this.initPanelAsTab, this);
39624 initPanelAsTab : function(panel){
39625 var ti = this.tabs.addTab(
39629 this.config.closeOnTab && panel.isClosable(),
39632 if(panel.tabTip !== undefined){
39633 ti.setTooltip(panel.tabTip);
39635 ti.on("activate", function(){
39636 this.setActivePanel(panel);
39639 if(this.config.closeOnTab){
39640 ti.on("beforeclose", function(t, e){
39642 this.remove(panel);
39646 panel.tabItem = ti;
39651 updatePanelTitle : function(panel, title)
39653 if(this.activePanel == panel){
39654 this.updateTitle(title);
39657 var ti = this.tabs.getTab(panel.getEl().id);
39659 if(panel.tabTip !== undefined){
39660 ti.setTooltip(panel.tabTip);
39665 updateTitle : function(title){
39666 if(this.titleTextEl && !this.config.title){
39667 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39671 setActivePanel : function(panel)
39673 panel = this.getPanel(panel);
39674 if(this.activePanel && this.activePanel != panel){
39675 if(this.activePanel.setActiveState(false) === false){
39679 this.activePanel = panel;
39680 panel.setActiveState(true);
39681 if(this.panelSize){
39682 panel.setSize(this.panelSize.width, this.panelSize.height);
39685 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39687 this.updateTitle(panel.getTitle());
39689 this.fireEvent("invalidated", this);
39691 this.fireEvent("panelactivated", this, panel);
39695 * Shows the specified panel.
39696 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39697 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39699 showPanel : function(panel)
39701 panel = this.getPanel(panel);
39704 var tab = this.tabs.getTab(panel.getEl().id);
39705 if(tab.isHidden()){
39706 this.tabs.unhideTab(tab.id);
39710 this.setActivePanel(panel);
39717 * Get the active panel for this region.
39718 * @return {Roo.ContentPanel} The active panel or null
39720 getActivePanel : function(){
39721 return this.activePanel;
39724 validateVisibility : function(){
39725 if(this.panels.getCount() < 1){
39726 this.updateTitle(" ");
39727 this.closeBtn.hide();
39730 if(!this.isVisible()){
39737 * Adds the passed ContentPanel(s) to this region.
39738 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39739 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39741 add : function(panel)
39743 if(arguments.length > 1){
39744 for(var i = 0, len = arguments.length; i < len; i++) {
39745 this.add(arguments[i]);
39750 // if we have not been rendered yet, then we can not really do much of this..
39751 if (!this.bodyEl) {
39752 this.unrendered_panels.push(panel);
39759 if(this.hasPanel(panel)){
39760 this.showPanel(panel);
39763 panel.setRegion(this);
39764 this.panels.add(panel);
39765 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39766 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39767 // and hide them... ???
39768 this.bodyEl.dom.appendChild(panel.getEl().dom);
39769 if(panel.background !== true){
39770 this.setActivePanel(panel);
39772 this.fireEvent("paneladded", this, panel);
39779 this.initPanelAsTab(panel);
39783 if(panel.background !== true){
39784 this.tabs.activate(panel.getEl().id);
39786 this.fireEvent("paneladded", this, panel);
39791 * Hides the tab for the specified panel.
39792 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39794 hidePanel : function(panel){
39795 if(this.tabs && (panel = this.getPanel(panel))){
39796 this.tabs.hideTab(panel.getEl().id);
39801 * Unhides the tab for a previously hidden panel.
39802 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39804 unhidePanel : function(panel){
39805 if(this.tabs && (panel = this.getPanel(panel))){
39806 this.tabs.unhideTab(panel.getEl().id);
39810 clearPanels : function(){
39811 while(this.panels.getCount() > 0){
39812 this.remove(this.panels.first());
39817 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39818 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39819 * @param {Boolean} preservePanel Overrides the config preservePanel option
39820 * @return {Roo.ContentPanel} The panel that was removed
39822 remove : function(panel, preservePanel)
39824 panel = this.getPanel(panel);
39829 this.fireEvent("beforeremove", this, panel, e);
39830 if(e.cancel === true){
39833 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39834 var panelId = panel.getId();
39835 this.panels.removeKey(panelId);
39837 document.body.appendChild(panel.getEl().dom);
39840 this.tabs.removeTab(panel.getEl().id);
39841 }else if (!preservePanel){
39842 this.bodyEl.dom.removeChild(panel.getEl().dom);
39844 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39845 var p = this.panels.first();
39846 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39847 tempEl.appendChild(p.getEl().dom);
39848 this.bodyEl.update("");
39849 this.bodyEl.dom.appendChild(p.getEl().dom);
39851 this.updateTitle(p.getTitle());
39853 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39854 this.setActivePanel(p);
39856 panel.setRegion(null);
39857 if(this.activePanel == panel){
39858 this.activePanel = null;
39860 if(this.config.autoDestroy !== false && preservePanel !== true){
39861 try{panel.destroy();}catch(e){}
39863 this.fireEvent("panelremoved", this, panel);
39868 * Returns the TabPanel component used by this region
39869 * @return {Roo.TabPanel}
39871 getTabs : function(){
39875 createTool : function(parentEl, className){
39876 var btn = Roo.DomHelper.append(parentEl, {
39878 cls: "x-layout-tools-button",
39881 cls: "roo-layout-tools-button-inner " + className,
39885 btn.addClassOnOver("roo-layout-tools-button-over");
39890 * Ext JS Library 1.1.1
39891 * Copyright(c) 2006-2007, Ext JS, LLC.
39893 * Originally Released Under LGPL - original licence link has changed is not relivant.
39896 * <script type="text/javascript">
39902 * @class Roo.SplitLayoutRegion
39903 * @extends Roo.LayoutRegion
39904 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39906 Roo.bootstrap.layout.Split = function(config){
39907 this.cursor = config.cursor;
39908 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39911 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39913 splitTip : "Drag to resize.",
39914 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39915 useSplitTips : false,
39917 applyConfig : function(config){
39918 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39921 onRender : function(ctr,pos) {
39923 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39924 if(!this.config.split){
39929 var splitEl = Roo.DomHelper.append(ctr.dom, {
39931 id: this.el.id + "-split",
39932 cls: "roo-layout-split roo-layout-split-"+this.position,
39935 /** The SplitBar for this region
39936 * @type Roo.SplitBar */
39937 // does not exist yet...
39938 Roo.log([this.position, this.orientation]);
39940 this.split = new Roo.bootstrap.SplitBar({
39941 dragElement : splitEl,
39942 resizingElement: this.el,
39943 orientation : this.orientation
39946 this.split.on("moved", this.onSplitMove, this);
39947 this.split.useShim = this.config.useShim === true;
39948 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39949 if(this.useSplitTips){
39950 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39952 //if(config.collapsible){
39953 // this.split.el.on("dblclick", this.collapse, this);
39956 if(typeof this.config.minSize != "undefined"){
39957 this.split.minSize = this.config.minSize;
39959 if(typeof this.config.maxSize != "undefined"){
39960 this.split.maxSize = this.config.maxSize;
39962 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39963 this.hideSplitter();
39968 getHMaxSize : function(){
39969 var cmax = this.config.maxSize || 10000;
39970 var center = this.mgr.getRegion("center");
39971 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39974 getVMaxSize : function(){
39975 var cmax = this.config.maxSize || 10000;
39976 var center = this.mgr.getRegion("center");
39977 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39980 onSplitMove : function(split, newSize){
39981 this.fireEvent("resized", this, newSize);
39985 * Returns the {@link Roo.SplitBar} for this region.
39986 * @return {Roo.SplitBar}
39988 getSplitBar : function(){
39993 this.hideSplitter();
39994 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39997 hideSplitter : function(){
39999 this.split.el.setLocation(-2000,-2000);
40000 this.split.el.hide();
40006 this.split.el.show();
40008 Roo.bootstrap.layout.Split.superclass.show.call(this);
40011 beforeSlide: function(){
40012 if(Roo.isGecko){// firefox overflow auto bug workaround
40013 this.bodyEl.clip();
40015 this.tabs.bodyEl.clip();
40017 if(this.activePanel){
40018 this.activePanel.getEl().clip();
40020 if(this.activePanel.beforeSlide){
40021 this.activePanel.beforeSlide();
40027 afterSlide : function(){
40028 if(Roo.isGecko){// firefox overflow auto bug workaround
40029 this.bodyEl.unclip();
40031 this.tabs.bodyEl.unclip();
40033 if(this.activePanel){
40034 this.activePanel.getEl().unclip();
40035 if(this.activePanel.afterSlide){
40036 this.activePanel.afterSlide();
40042 initAutoHide : function(){
40043 if(this.autoHide !== false){
40044 if(!this.autoHideHd){
40045 var st = new Roo.util.DelayedTask(this.slideIn, this);
40046 this.autoHideHd = {
40047 "mouseout": function(e){
40048 if(!e.within(this.el, true)){
40052 "mouseover" : function(e){
40058 this.el.on(this.autoHideHd);
40062 clearAutoHide : function(){
40063 if(this.autoHide !== false){
40064 this.el.un("mouseout", this.autoHideHd.mouseout);
40065 this.el.un("mouseover", this.autoHideHd.mouseover);
40069 clearMonitor : function(){
40070 Roo.get(document).un("click", this.slideInIf, this);
40073 // these names are backwards but not changed for compat
40074 slideOut : function(){
40075 if(this.isSlid || this.el.hasActiveFx()){
40078 this.isSlid = true;
40079 if(this.collapseBtn){
40080 this.collapseBtn.hide();
40082 this.closeBtnState = this.closeBtn.getStyle('display');
40083 this.closeBtn.hide();
40085 this.stickBtn.show();
40088 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40089 this.beforeSlide();
40090 this.el.setStyle("z-index", 10001);
40091 this.el.slideIn(this.getSlideAnchor(), {
40092 callback: function(){
40094 this.initAutoHide();
40095 Roo.get(document).on("click", this.slideInIf, this);
40096 this.fireEvent("slideshow", this);
40103 afterSlideIn : function(){
40104 this.clearAutoHide();
40105 this.isSlid = false;
40106 this.clearMonitor();
40107 this.el.setStyle("z-index", "");
40108 if(this.collapseBtn){
40109 this.collapseBtn.show();
40111 this.closeBtn.setStyle('display', this.closeBtnState);
40113 this.stickBtn.hide();
40115 this.fireEvent("slidehide", this);
40118 slideIn : function(cb){
40119 if(!this.isSlid || this.el.hasActiveFx()){
40123 this.isSlid = false;
40124 this.beforeSlide();
40125 this.el.slideOut(this.getSlideAnchor(), {
40126 callback: function(){
40127 this.el.setLeftTop(-10000, -10000);
40129 this.afterSlideIn();
40137 slideInIf : function(e){
40138 if(!e.within(this.el)){
40143 animateCollapse : function(){
40144 this.beforeSlide();
40145 this.el.setStyle("z-index", 20000);
40146 var anchor = this.getSlideAnchor();
40147 this.el.slideOut(anchor, {
40148 callback : function(){
40149 this.el.setStyle("z-index", "");
40150 this.collapsedEl.slideIn(anchor, {duration:.3});
40152 this.el.setLocation(-10000,-10000);
40154 this.fireEvent("collapsed", this);
40161 animateExpand : function(){
40162 this.beforeSlide();
40163 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40164 this.el.setStyle("z-index", 20000);
40165 this.collapsedEl.hide({
40168 this.el.slideIn(this.getSlideAnchor(), {
40169 callback : function(){
40170 this.el.setStyle("z-index", "");
40173 this.split.el.show();
40175 this.fireEvent("invalidated", this);
40176 this.fireEvent("expanded", this);
40204 getAnchor : function(){
40205 return this.anchors[this.position];
40208 getCollapseAnchor : function(){
40209 return this.canchors[this.position];
40212 getSlideAnchor : function(){
40213 return this.sanchors[this.position];
40216 getAlignAdj : function(){
40217 var cm = this.cmargins;
40218 switch(this.position){
40234 getExpandAdj : function(){
40235 var c = this.collapsedEl, cm = this.cmargins;
40236 switch(this.position){
40238 return [-(cm.right+c.getWidth()+cm.left), 0];
40241 return [cm.right+c.getWidth()+cm.left, 0];
40244 return [0, -(cm.top+cm.bottom+c.getHeight())];
40247 return [0, cm.top+cm.bottom+c.getHeight()];
40253 * Ext JS Library 1.1.1
40254 * Copyright(c) 2006-2007, Ext JS, LLC.
40256 * Originally Released Under LGPL - original licence link has changed is not relivant.
40259 * <script type="text/javascript">
40262 * These classes are private internal classes
40264 Roo.bootstrap.layout.Center = function(config){
40265 config.region = "center";
40266 Roo.bootstrap.layout.Region.call(this, config);
40267 this.visible = true;
40268 this.minWidth = config.minWidth || 20;
40269 this.minHeight = config.minHeight || 20;
40272 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40274 // center panel can't be hidden
40278 // center panel can't be hidden
40281 getMinWidth: function(){
40282 return this.minWidth;
40285 getMinHeight: function(){
40286 return this.minHeight;
40300 Roo.bootstrap.layout.North = function(config)
40302 config.region = 'north';
40303 config.cursor = 'n-resize';
40305 Roo.bootstrap.layout.Split.call(this, config);
40309 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40310 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40311 this.split.el.addClass("roo-layout-split-v");
40313 //var size = config.initialSize || config.height;
40314 //if(this.el && typeof size != "undefined"){
40315 // this.el.setHeight(size);
40318 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40320 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40323 onRender : function(ctr, pos)
40325 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40326 var size = this.config.initialSize || this.config.height;
40327 if(this.el && typeof size != "undefined"){
40328 this.el.setHeight(size);
40333 getBox : function(){
40334 if(this.collapsed){
40335 return this.collapsedEl.getBox();
40337 var box = this.el.getBox();
40339 box.height += this.split.el.getHeight();
40344 updateBox : function(box){
40345 if(this.split && !this.collapsed){
40346 box.height -= this.split.el.getHeight();
40347 this.split.el.setLeft(box.x);
40348 this.split.el.setTop(box.y+box.height);
40349 this.split.el.setWidth(box.width);
40351 if(this.collapsed){
40352 this.updateBody(box.width, null);
40354 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40362 Roo.bootstrap.layout.South = function(config){
40363 config.region = 'south';
40364 config.cursor = 's-resize';
40365 Roo.bootstrap.layout.Split.call(this, config);
40367 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40368 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40369 this.split.el.addClass("roo-layout-split-v");
40374 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40375 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40377 onRender : function(ctr, pos)
40379 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40380 var size = this.config.initialSize || this.config.height;
40381 if(this.el && typeof size != "undefined"){
40382 this.el.setHeight(size);
40387 getBox : function(){
40388 if(this.collapsed){
40389 return this.collapsedEl.getBox();
40391 var box = this.el.getBox();
40393 var sh = this.split.el.getHeight();
40400 updateBox : function(box){
40401 if(this.split && !this.collapsed){
40402 var sh = this.split.el.getHeight();
40405 this.split.el.setLeft(box.x);
40406 this.split.el.setTop(box.y-sh);
40407 this.split.el.setWidth(box.width);
40409 if(this.collapsed){
40410 this.updateBody(box.width, null);
40412 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40416 Roo.bootstrap.layout.East = function(config){
40417 config.region = "east";
40418 config.cursor = "e-resize";
40419 Roo.bootstrap.layout.Split.call(this, config);
40421 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40422 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40423 this.split.el.addClass("roo-layout-split-h");
40427 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40428 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40430 onRender : function(ctr, pos)
40432 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40433 var size = this.config.initialSize || this.config.width;
40434 if(this.el && typeof size != "undefined"){
40435 this.el.setWidth(size);
40440 getBox : function(){
40441 if(this.collapsed){
40442 return this.collapsedEl.getBox();
40444 var box = this.el.getBox();
40446 var sw = this.split.el.getWidth();
40453 updateBox : function(box){
40454 if(this.split && !this.collapsed){
40455 var sw = this.split.el.getWidth();
40457 this.split.el.setLeft(box.x);
40458 this.split.el.setTop(box.y);
40459 this.split.el.setHeight(box.height);
40462 if(this.collapsed){
40463 this.updateBody(null, box.height);
40465 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40469 Roo.bootstrap.layout.West = function(config){
40470 config.region = "west";
40471 config.cursor = "w-resize";
40473 Roo.bootstrap.layout.Split.call(this, config);
40475 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40476 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40477 this.split.el.addClass("roo-layout-split-h");
40481 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40482 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40484 onRender: function(ctr, pos)
40486 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40487 var size = this.config.initialSize || this.config.width;
40488 if(typeof size != "undefined"){
40489 this.el.setWidth(size);
40493 getBox : function(){
40494 if(this.collapsed){
40495 return this.collapsedEl.getBox();
40497 var box = this.el.getBox();
40498 if (box.width == 0) {
40499 box.width = this.config.width; // kludge?
40502 box.width += this.split.el.getWidth();
40507 updateBox : function(box){
40508 if(this.split && !this.collapsed){
40509 var sw = this.split.el.getWidth();
40511 this.split.el.setLeft(box.x+box.width);
40512 this.split.el.setTop(box.y);
40513 this.split.el.setHeight(box.height);
40515 if(this.collapsed){
40516 this.updateBody(null, box.height);
40518 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40520 });Roo.namespace("Roo.bootstrap.panel");/*
40522 * Ext JS Library 1.1.1
40523 * Copyright(c) 2006-2007, Ext JS, LLC.
40525 * Originally Released Under LGPL - original licence link has changed is not relivant.
40528 * <script type="text/javascript">
40531 * @class Roo.ContentPanel
40532 * @extends Roo.util.Observable
40533 * A basic ContentPanel element.
40534 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40535 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40536 * @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
40537 * @cfg {Boolean} closable True if the panel can be closed/removed
40538 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40539 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40540 * @cfg {Toolbar} toolbar A toolbar for this panel
40541 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40542 * @cfg {String} title The title for this panel
40543 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40544 * @cfg {String} url Calls {@link #setUrl} with this value
40545 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40546 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40547 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40548 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40549 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40550 * @cfg {Boolean} badges render the badges
40551 * @cfg {String} cls extra classes to use
40552 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40555 * Create a new ContentPanel.
40556 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40557 * @param {String/Object} config A string to set only the title or a config object
40558 * @param {String} content (optional) Set the HTML content for this panel
40559 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40561 Roo.bootstrap.panel.Content = function( config){
40563 this.tpl = config.tpl || false;
40565 var el = config.el;
40566 var content = config.content;
40568 if(config.autoCreate){ // xtype is available if this is called from factory
40571 this.el = Roo.get(el);
40572 if(!this.el && config && config.autoCreate){
40573 if(typeof config.autoCreate == "object"){
40574 if(!config.autoCreate.id){
40575 config.autoCreate.id = config.id||el;
40577 this.el = Roo.DomHelper.append(document.body,
40578 config.autoCreate, true);
40582 cls: (config.cls || '') +
40583 (config.background ? ' bg-' + config.background : '') +
40584 " roo-layout-inactive-content",
40587 if (config.iframe) {
40591 style : 'border: 0px',
40592 src : 'about:blank'
40598 elcfg.html = config.html;
40602 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40603 if (config.iframe) {
40604 this.iframeEl = this.el.select('iframe',true).first();
40609 this.closable = false;
40610 this.loaded = false;
40611 this.active = false;
40614 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40616 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40618 this.wrapEl = this.el; //this.el.wrap();
40620 if (config.toolbar.items) {
40621 ti = config.toolbar.items ;
40622 delete config.toolbar.items ;
40626 this.toolbar.render(this.wrapEl, 'before');
40627 for(var i =0;i < ti.length;i++) {
40628 // Roo.log(['add child', items[i]]);
40629 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40631 this.toolbar.items = nitems;
40632 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40633 delete config.toolbar;
40637 // xtype created footer. - not sure if will work as we normally have to render first..
40638 if (this.footer && !this.footer.el && this.footer.xtype) {
40639 if (!this.wrapEl) {
40640 this.wrapEl = this.el.wrap();
40643 this.footer.container = this.wrapEl.createChild();
40645 this.footer = Roo.factory(this.footer, Roo);
40650 if(typeof config == "string"){
40651 this.title = config;
40653 Roo.apply(this, config);
40657 this.resizeEl = Roo.get(this.resizeEl, true);
40659 this.resizeEl = this.el;
40661 // handle view.xtype
40669 * Fires when this panel is activated.
40670 * @param {Roo.ContentPanel} this
40674 * @event deactivate
40675 * Fires when this panel is activated.
40676 * @param {Roo.ContentPanel} this
40678 "deactivate" : true,
40682 * Fires when this panel is resized if fitToFrame is true.
40683 * @param {Roo.ContentPanel} this
40684 * @param {Number} width The width after any component adjustments
40685 * @param {Number} height The height after any component adjustments
40691 * Fires when this tab is created
40692 * @param {Roo.ContentPanel} this
40698 * Fires when this content is scrolled
40699 * @param {Roo.ContentPanel} this
40700 * @param {Event} scrollEvent
40711 if(this.autoScroll && !this.iframe){
40712 this.resizeEl.setStyle("overflow", "auto");
40713 this.resizeEl.on('scroll', this.onScroll, this);
40715 // fix randome scrolling
40716 //this.el.on('scroll', function() {
40717 // Roo.log('fix random scolling');
40718 // this.scrollTo('top',0);
40721 content = content || this.content;
40723 this.setContent(content);
40725 if(config && config.url){
40726 this.setUrl(this.url, this.params, this.loadOnce);
40731 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40733 if (this.view && typeof(this.view.xtype) != 'undefined') {
40734 this.view.el = this.el.appendChild(document.createElement("div"));
40735 this.view = Roo.factory(this.view);
40736 this.view.render && this.view.render(false, '');
40740 this.fireEvent('render', this);
40743 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40753 /* Resize Element - use this to work out scroll etc. */
40756 setRegion : function(region){
40757 this.region = region;
40758 this.setActiveClass(region && !this.background);
40762 setActiveClass: function(state)
40765 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40766 this.el.setStyle('position','relative');
40768 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40769 this.el.setStyle('position', 'absolute');
40774 * Returns the toolbar for this Panel if one was configured.
40775 * @return {Roo.Toolbar}
40777 getToolbar : function(){
40778 return this.toolbar;
40781 setActiveState : function(active)
40783 this.active = active;
40784 this.setActiveClass(active);
40786 if(this.fireEvent("deactivate", this) === false){
40791 this.fireEvent("activate", this);
40795 * Updates this panel's element (not for iframe)
40796 * @param {String} content The new content
40797 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40799 setContent : function(content, loadScripts){
40804 this.el.update(content, loadScripts);
40807 ignoreResize : function(w, h){
40808 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40811 this.lastSize = {width: w, height: h};
40816 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40817 * @return {Roo.UpdateManager} The UpdateManager
40819 getUpdateManager : function(){
40823 return this.el.getUpdateManager();
40826 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40827 * Does not work with IFRAME contents
40828 * @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:
40831 url: "your-url.php",
40832 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40833 callback: yourFunction,
40834 scope: yourObject, //(optional scope)
40837 text: "Loading...",
40843 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40844 * 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.
40845 * @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}
40846 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40847 * @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.
40848 * @return {Roo.ContentPanel} this
40856 var um = this.el.getUpdateManager();
40857 um.update.apply(um, arguments);
40863 * 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.
40864 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40865 * @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)
40866 * @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)
40867 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40869 setUrl : function(url, params, loadOnce){
40871 this.iframeEl.dom.src = url;
40875 if(this.refreshDelegate){
40876 this.removeListener("activate", this.refreshDelegate);
40878 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40879 this.on("activate", this.refreshDelegate);
40880 return this.el.getUpdateManager();
40883 _handleRefresh : function(url, params, loadOnce){
40884 if(!loadOnce || !this.loaded){
40885 var updater = this.el.getUpdateManager();
40886 updater.update(url, params, this._setLoaded.createDelegate(this));
40890 _setLoaded : function(){
40891 this.loaded = true;
40895 * Returns this panel's id
40898 getId : function(){
40903 * Returns this panel's element - used by regiosn to add.
40904 * @return {Roo.Element}
40906 getEl : function(){
40907 return this.wrapEl || this.el;
40912 adjustForComponents : function(width, height)
40914 //Roo.log('adjustForComponents ');
40915 if(this.resizeEl != this.el){
40916 width -= this.el.getFrameWidth('lr');
40917 height -= this.el.getFrameWidth('tb');
40920 var te = this.toolbar.getEl();
40921 te.setWidth(width);
40922 height -= te.getHeight();
40925 var te = this.footer.getEl();
40926 te.setWidth(width);
40927 height -= te.getHeight();
40931 if(this.adjustments){
40932 width += this.adjustments[0];
40933 height += this.adjustments[1];
40935 return {"width": width, "height": height};
40938 setSize : function(width, height){
40939 if(this.fitToFrame && !this.ignoreResize(width, height)){
40940 if(this.fitContainer && this.resizeEl != this.el){
40941 this.el.setSize(width, height);
40943 var size = this.adjustForComponents(width, height);
40945 this.iframeEl.setSize(width,height);
40948 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40949 this.fireEvent('resize', this, size.width, size.height);
40956 * Returns this panel's title
40959 getTitle : function(){
40961 if (typeof(this.title) != 'object') {
40966 for (var k in this.title) {
40967 if (!this.title.hasOwnProperty(k)) {
40971 if (k.indexOf('-') >= 0) {
40972 var s = k.split('-');
40973 for (var i = 0; i<s.length; i++) {
40974 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40977 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40984 * Set this panel's title
40985 * @param {String} title
40987 setTitle : function(title){
40988 this.title = title;
40990 this.region.updatePanelTitle(this, title);
40995 * Returns true is this panel was configured to be closable
40996 * @return {Boolean}
40998 isClosable : function(){
40999 return this.closable;
41002 beforeSlide : function(){
41004 this.resizeEl.clip();
41007 afterSlide : function(){
41009 this.resizeEl.unclip();
41013 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41014 * Will fail silently if the {@link #setUrl} method has not been called.
41015 * This does not activate the panel, just updates its content.
41017 refresh : function(){
41018 if(this.refreshDelegate){
41019 this.loaded = false;
41020 this.refreshDelegate();
41025 * Destroys this panel
41027 destroy : function(){
41028 this.el.removeAllListeners();
41029 var tempEl = document.createElement("span");
41030 tempEl.appendChild(this.el.dom);
41031 tempEl.innerHTML = "";
41037 * form - if the content panel contains a form - this is a reference to it.
41038 * @type {Roo.form.Form}
41042 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41043 * This contains a reference to it.
41049 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41059 * @param {Object} cfg Xtype definition of item to add.
41063 getChildContainer: function () {
41064 return this.getEl();
41068 onScroll : function(e)
41070 this.fireEvent('scroll', this, e);
41075 var ret = new Roo.factory(cfg);
41080 if (cfg.xtype.match(/^Form$/)) {
41083 //if (this.footer) {
41084 // el = this.footer.container.insertSibling(false, 'before');
41086 el = this.el.createChild();
41089 this.form = new Roo.form.Form(cfg);
41092 if ( this.form.allItems.length) {
41093 this.form.render(el.dom);
41097 // should only have one of theses..
41098 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41099 // views.. should not be just added - used named prop 'view''
41101 cfg.el = this.el.appendChild(document.createElement("div"));
41104 var ret = new Roo.factory(cfg);
41106 ret.render && ret.render(false, ''); // render blank..
41116 * @class Roo.bootstrap.panel.Grid
41117 * @extends Roo.bootstrap.panel.Content
41119 * Create a new GridPanel.
41120 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41121 * @param {Object} config A the config object
41127 Roo.bootstrap.panel.Grid = function(config)
41131 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41132 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41134 config.el = this.wrapper;
41135 //this.el = this.wrapper;
41137 if (config.container) {
41138 // ctor'ed from a Border/panel.grid
41141 this.wrapper.setStyle("overflow", "hidden");
41142 this.wrapper.addClass('roo-grid-container');
41147 if(config.toolbar){
41148 var tool_el = this.wrapper.createChild();
41149 this.toolbar = Roo.factory(config.toolbar);
41151 if (config.toolbar.items) {
41152 ti = config.toolbar.items ;
41153 delete config.toolbar.items ;
41157 this.toolbar.render(tool_el);
41158 for(var i =0;i < ti.length;i++) {
41159 // Roo.log(['add child', items[i]]);
41160 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41162 this.toolbar.items = nitems;
41164 delete config.toolbar;
41167 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41168 config.grid.scrollBody = true;;
41169 config.grid.monitorWindowResize = false; // turn off autosizing
41170 config.grid.autoHeight = false;
41171 config.grid.autoWidth = false;
41173 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41175 if (config.background) {
41176 // render grid on panel activation (if panel background)
41177 this.on('activate', function(gp) {
41178 if (!gp.grid.rendered) {
41179 gp.grid.render(this.wrapper);
41180 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41185 this.grid.render(this.wrapper);
41186 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41189 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41190 // ??? needed ??? config.el = this.wrapper;
41195 // xtype created footer. - not sure if will work as we normally have to render first..
41196 if (this.footer && !this.footer.el && this.footer.xtype) {
41198 var ctr = this.grid.getView().getFooterPanel(true);
41199 this.footer.dataSource = this.grid.dataSource;
41200 this.footer = Roo.factory(this.footer, Roo);
41201 this.footer.render(ctr);
41211 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41212 getId : function(){
41213 return this.grid.id;
41217 * Returns the grid for this panel
41218 * @return {Roo.bootstrap.Table}
41220 getGrid : function(){
41224 setSize : function(width, height){
41225 if(!this.ignoreResize(width, height)){
41226 var grid = this.grid;
41227 var size = this.adjustForComponents(width, height);
41228 // tfoot is not a footer?
41231 var gridel = grid.getGridEl();
41232 gridel.setSize(size.width, size.height);
41234 var tbd = grid.getGridEl().select('tbody', true).first();
41235 var thd = grid.getGridEl().select('thead',true).first();
41236 var tbf= grid.getGridEl().select('tfoot', true).first();
41239 size.height -= tbf.getHeight();
41242 size.height -= thd.getHeight();
41245 tbd.setSize(size.width, size.height );
41246 // this is for the account management tab -seems to work there.
41247 var thd = grid.getGridEl().select('thead',true).first();
41249 // tbd.setSize(size.width, size.height - thd.getHeight());
41258 beforeSlide : function(){
41259 this.grid.getView().scroller.clip();
41262 afterSlide : function(){
41263 this.grid.getView().scroller.unclip();
41266 destroy : function(){
41267 this.grid.destroy();
41269 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41274 * @class Roo.bootstrap.panel.Nest
41275 * @extends Roo.bootstrap.panel.Content
41277 * Create a new Panel, that can contain a layout.Border.
41280 * @param {Roo.BorderLayout} layout The layout for this panel
41281 * @param {String/Object} config A string to set only the title or a config object
41283 Roo.bootstrap.panel.Nest = function(config)
41285 // construct with only one argument..
41286 /* FIXME - implement nicer consturctors
41287 if (layout.layout) {
41289 layout = config.layout;
41290 delete config.layout;
41292 if (layout.xtype && !layout.getEl) {
41293 // then layout needs constructing..
41294 layout = Roo.factory(layout, Roo);
41298 config.el = config.layout.getEl();
41300 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41302 config.layout.monitorWindowResize = false; // turn off autosizing
41303 this.layout = config.layout;
41304 this.layout.getEl().addClass("roo-layout-nested-layout");
41305 this.layout.parent = this;
41312 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41314 setSize : function(width, height){
41315 if(!this.ignoreResize(width, height)){
41316 var size = this.adjustForComponents(width, height);
41317 var el = this.layout.getEl();
41318 if (size.height < 1) {
41319 el.setWidth(size.width);
41321 el.setSize(size.width, size.height);
41323 var touch = el.dom.offsetWidth;
41324 this.layout.layout();
41325 // ie requires a double layout on the first pass
41326 if(Roo.isIE && !this.initialized){
41327 this.initialized = true;
41328 this.layout.layout();
41333 // activate all subpanels if not currently active..
41335 setActiveState : function(active){
41336 this.active = active;
41337 this.setActiveClass(active);
41340 this.fireEvent("deactivate", this);
41344 this.fireEvent("activate", this);
41345 // not sure if this should happen before or after..
41346 if (!this.layout) {
41347 return; // should not happen..
41350 for (var r in this.layout.regions) {
41351 reg = this.layout.getRegion(r);
41352 if (reg.getActivePanel()) {
41353 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41354 reg.setActivePanel(reg.getActivePanel());
41357 if (!reg.panels.length) {
41360 reg.showPanel(reg.getPanel(0));
41369 * Returns the nested BorderLayout for this panel
41370 * @return {Roo.BorderLayout}
41372 getLayout : function(){
41373 return this.layout;
41377 * Adds a xtype elements to the layout of the nested panel
41381 xtype : 'ContentPanel',
41388 xtype : 'NestedLayoutPanel',
41394 items : [ ... list of content panels or nested layout panels.. ]
41398 * @param {Object} cfg Xtype definition of item to add.
41400 addxtype : function(cfg) {
41401 return this.layout.addxtype(cfg);
41406 * Ext JS Library 1.1.1
41407 * Copyright(c) 2006-2007, Ext JS, LLC.
41409 * Originally Released Under LGPL - original licence link has changed is not relivant.
41412 * <script type="text/javascript">
41415 * @class Roo.TabPanel
41416 * @extends Roo.util.Observable
41417 * A lightweight tab container.
41421 // basic tabs 1, built from existing content
41422 var tabs = new Roo.TabPanel("tabs1");
41423 tabs.addTab("script", "View Script");
41424 tabs.addTab("markup", "View Markup");
41425 tabs.activate("script");
41427 // more advanced tabs, built from javascript
41428 var jtabs = new Roo.TabPanel("jtabs");
41429 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41431 // set up the UpdateManager
41432 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41433 var updater = tab2.getUpdateManager();
41434 updater.setDefaultUrl("ajax1.htm");
41435 tab2.on('activate', updater.refresh, updater, true);
41437 // Use setUrl for Ajax loading
41438 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41439 tab3.setUrl("ajax2.htm", null, true);
41442 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41445 jtabs.activate("jtabs-1");
41448 * Create a new TabPanel.
41449 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41450 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41452 Roo.bootstrap.panel.Tabs = function(config){
41454 * The container element for this TabPanel.
41455 * @type Roo.Element
41457 this.el = Roo.get(config.el);
41460 if(typeof config == "boolean"){
41461 this.tabPosition = config ? "bottom" : "top";
41463 Roo.apply(this, config);
41467 if(this.tabPosition == "bottom"){
41468 // if tabs are at the bottom = create the body first.
41469 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41470 this.el.addClass("roo-tabs-bottom");
41472 // next create the tabs holders
41474 if (this.tabPosition == "west"){
41476 var reg = this.region; // fake it..
41478 if (!reg.mgr.parent) {
41481 reg = reg.mgr.parent.region;
41483 Roo.log("got nest?");
41485 if (reg.mgr.getRegion('west')) {
41486 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41487 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41488 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41489 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41490 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41498 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41499 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41500 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41501 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41506 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41509 // finally - if tabs are at the top, then create the body last..
41510 if(this.tabPosition != "bottom"){
41511 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41512 * @type Roo.Element
41514 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41515 this.el.addClass("roo-tabs-top");
41519 this.bodyEl.setStyle("position", "relative");
41521 this.active = null;
41522 this.activateDelegate = this.activate.createDelegate(this);
41527 * Fires when the active tab changes
41528 * @param {Roo.TabPanel} this
41529 * @param {Roo.TabPanelItem} activePanel The new active tab
41533 * @event beforetabchange
41534 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41535 * @param {Roo.TabPanel} this
41536 * @param {Object} e Set cancel to true on this object to cancel the tab change
41537 * @param {Roo.TabPanelItem} tab The tab being changed to
41539 "beforetabchange" : true
41542 Roo.EventManager.onWindowResize(this.onResize, this);
41543 this.cpad = this.el.getPadding("lr");
41544 this.hiddenCount = 0;
41547 // toolbar on the tabbar support...
41548 if (this.toolbar) {
41549 alert("no toolbar support yet");
41550 this.toolbar = false;
41552 var tcfg = this.toolbar;
41553 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41554 this.toolbar = new Roo.Toolbar(tcfg);
41555 if (Roo.isSafari) {
41556 var tbl = tcfg.container.child('table', true);
41557 tbl.setAttribute('width', '100%');
41565 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41568 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41570 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41572 tabPosition : "top",
41574 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41576 currentTabWidth : 0,
41578 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41582 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41586 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41588 preferredTabWidth : 175,
41590 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41592 resizeTabs : false,
41594 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41596 monitorResize : true,
41598 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41600 toolbar : false, // set by caller..
41602 region : false, /// set by caller
41604 disableTooltips : true, // not used yet...
41607 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41608 * @param {String} id The id of the div to use <b>or create</b>
41609 * @param {String} text The text for the tab
41610 * @param {String} content (optional) Content to put in the TabPanelItem body
41611 * @param {Boolean} closable (optional) True to create a close icon on the tab
41612 * @return {Roo.TabPanelItem} The created TabPanelItem
41614 addTab : function(id, text, content, closable, tpl)
41616 var item = new Roo.bootstrap.panel.TabItem({
41620 closable : closable,
41623 this.addTabItem(item);
41625 item.setContent(content);
41631 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41632 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41633 * @return {Roo.TabPanelItem}
41635 getTab : function(id){
41636 return this.items[id];
41640 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41641 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41643 hideTab : function(id){
41644 var t = this.items[id];
41647 this.hiddenCount++;
41648 this.autoSizeTabs();
41653 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41654 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41656 unhideTab : function(id){
41657 var t = this.items[id];
41659 t.setHidden(false);
41660 this.hiddenCount--;
41661 this.autoSizeTabs();
41666 * Adds an existing {@link Roo.TabPanelItem}.
41667 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41669 addTabItem : function(item)
41671 this.items[item.id] = item;
41672 this.items.push(item);
41673 this.autoSizeTabs();
41674 // if(this.resizeTabs){
41675 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41676 // this.autoSizeTabs();
41678 // item.autoSize();
41683 * Removes a {@link Roo.TabPanelItem}.
41684 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41686 removeTab : function(id){
41687 var items = this.items;
41688 var tab = items[id];
41689 if(!tab) { return; }
41690 var index = items.indexOf(tab);
41691 if(this.active == tab && items.length > 1){
41692 var newTab = this.getNextAvailable(index);
41697 this.stripEl.dom.removeChild(tab.pnode.dom);
41698 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41699 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41701 items.splice(index, 1);
41702 delete this.items[tab.id];
41703 tab.fireEvent("close", tab);
41704 tab.purgeListeners();
41705 this.autoSizeTabs();
41708 getNextAvailable : function(start){
41709 var items = this.items;
41711 // look for a next tab that will slide over to
41712 // replace the one being removed
41713 while(index < items.length){
41714 var item = items[++index];
41715 if(item && !item.isHidden()){
41719 // if one isn't found select the previous tab (on the left)
41722 var item = items[--index];
41723 if(item && !item.isHidden()){
41731 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41732 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41734 disableTab : function(id){
41735 var tab = this.items[id];
41736 if(tab && this.active != tab){
41742 * Enables a {@link Roo.TabPanelItem} that is disabled.
41743 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41745 enableTab : function(id){
41746 var tab = this.items[id];
41751 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41752 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41753 * @return {Roo.TabPanelItem} The TabPanelItem.
41755 activate : function(id)
41757 //Roo.log('activite:' + id);
41759 var tab = this.items[id];
41763 if(tab == this.active || tab.disabled){
41767 this.fireEvent("beforetabchange", this, e, tab);
41768 if(e.cancel !== true && !tab.disabled){
41770 this.active.hide();
41772 this.active = this.items[id];
41773 this.active.show();
41774 this.fireEvent("tabchange", this, this.active);
41780 * Gets the active {@link Roo.TabPanelItem}.
41781 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41783 getActiveTab : function(){
41784 return this.active;
41788 * Updates the tab body element to fit the height of the container element
41789 * for overflow scrolling
41790 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41792 syncHeight : function(targetHeight){
41793 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41794 var bm = this.bodyEl.getMargins();
41795 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41796 this.bodyEl.setHeight(newHeight);
41800 onResize : function(){
41801 if(this.monitorResize){
41802 this.autoSizeTabs();
41807 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41809 beginUpdate : function(){
41810 this.updating = true;
41814 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41816 endUpdate : function(){
41817 this.updating = false;
41818 this.autoSizeTabs();
41822 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41824 autoSizeTabs : function()
41826 var count = this.items.length;
41827 var vcount = count - this.hiddenCount;
41830 this.stripEl.hide();
41832 this.stripEl.show();
41835 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41840 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41841 var availWidth = Math.floor(w / vcount);
41842 var b = this.stripBody;
41843 if(b.getWidth() > w){
41844 var tabs = this.items;
41845 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41846 if(availWidth < this.minTabWidth){
41847 /*if(!this.sleft){ // incomplete scrolling code
41848 this.createScrollButtons();
41851 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41854 if(this.currentTabWidth < this.preferredTabWidth){
41855 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41861 * Returns the number of tabs in this TabPanel.
41864 getCount : function(){
41865 return this.items.length;
41869 * Resizes all the tabs to the passed width
41870 * @param {Number} The new width
41872 setTabWidth : function(width){
41873 this.currentTabWidth = width;
41874 for(var i = 0, len = this.items.length; i < len; i++) {
41875 if(!this.items[i].isHidden()) {
41876 this.items[i].setWidth(width);
41882 * Destroys this TabPanel
41883 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41885 destroy : function(removeEl){
41886 Roo.EventManager.removeResizeListener(this.onResize, this);
41887 for(var i = 0, len = this.items.length; i < len; i++){
41888 this.items[i].purgeListeners();
41890 if(removeEl === true){
41891 this.el.update("");
41896 createStrip : function(container)
41898 var strip = document.createElement("nav");
41899 strip.className = Roo.bootstrap.version == 4 ?
41900 "navbar-light bg-light" :
41901 "navbar navbar-default"; //"x-tabs-wrap";
41902 container.appendChild(strip);
41906 createStripList : function(strip)
41908 // div wrapper for retard IE
41909 // returns the "tr" element.
41910 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41911 //'<div class="x-tabs-strip-wrap">'+
41912 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41913 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41914 return strip.firstChild; //.firstChild.firstChild.firstChild;
41916 createBody : function(container)
41918 var body = document.createElement("div");
41919 Roo.id(body, "tab-body");
41920 //Roo.fly(body).addClass("x-tabs-body");
41921 Roo.fly(body).addClass("tab-content");
41922 container.appendChild(body);
41925 createItemBody :function(bodyEl, id){
41926 var body = Roo.getDom(id);
41928 body = document.createElement("div");
41931 //Roo.fly(body).addClass("x-tabs-item-body");
41932 Roo.fly(body).addClass("tab-pane");
41933 bodyEl.insertBefore(body, bodyEl.firstChild);
41937 createStripElements : function(stripEl, text, closable, tpl)
41939 var td = document.createElement("li"); // was td..
41940 td.className = 'nav-item';
41942 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41945 stripEl.appendChild(td);
41947 td.className = "x-tabs-closable";
41948 if(!this.closeTpl){
41949 this.closeTpl = new Roo.Template(
41950 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41951 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41952 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41955 var el = this.closeTpl.overwrite(td, {"text": text});
41956 var close = el.getElementsByTagName("div")[0];
41957 var inner = el.getElementsByTagName("em")[0];
41958 return {"el": el, "close": close, "inner": inner};
41961 // not sure what this is..
41962 // if(!this.tabTpl){
41963 //this.tabTpl = new Roo.Template(
41964 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41965 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41967 // this.tabTpl = new Roo.Template(
41968 // '<a href="#">' +
41969 // '<span unselectable="on"' +
41970 // (this.disableTooltips ? '' : ' title="{text}"') +
41971 // ' >{text}</span></a>'
41977 var template = tpl || this.tabTpl || false;
41980 template = new Roo.Template(
41981 Roo.bootstrap.version == 4 ?
41983 '<a class="nav-link" href="#" unselectable="on"' +
41984 (this.disableTooltips ? '' : ' title="{text}"') +
41987 '<a class="nav-link" href="#">' +
41988 '<span unselectable="on"' +
41989 (this.disableTooltips ? '' : ' title="{text}"') +
41990 ' >{text}</span></a>'
41995 switch (typeof(template)) {
41999 template = new Roo.Template(template);
42005 var el = template.overwrite(td, {"text": text});
42007 var inner = el.getElementsByTagName("span")[0];
42009 return {"el": el, "inner": inner};
42017 * @class Roo.TabPanelItem
42018 * @extends Roo.util.Observable
42019 * Represents an individual item (tab plus body) in a TabPanel.
42020 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42021 * @param {String} id The id of this TabPanelItem
42022 * @param {String} text The text for the tab of this TabPanelItem
42023 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42025 Roo.bootstrap.panel.TabItem = function(config){
42027 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42028 * @type Roo.TabPanel
42030 this.tabPanel = config.panel;
42032 * The id for this TabPanelItem
42035 this.id = config.id;
42037 this.disabled = false;
42039 this.text = config.text;
42041 this.loaded = false;
42042 this.closable = config.closable;
42045 * The body element for this TabPanelItem.
42046 * @type Roo.Element
42048 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42049 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42050 this.bodyEl.setStyle("display", "block");
42051 this.bodyEl.setStyle("zoom", "1");
42052 //this.hideAction();
42054 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42056 this.el = Roo.get(els.el);
42057 this.inner = Roo.get(els.inner, true);
42058 this.textEl = Roo.bootstrap.version == 4 ?
42059 this.el : Roo.get(this.el.dom.firstChild, true);
42061 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42062 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42065 // this.el.on("mousedown", this.onTabMouseDown, this);
42066 this.el.on("click", this.onTabClick, this);
42068 if(config.closable){
42069 var c = Roo.get(els.close, true);
42070 c.dom.title = this.closeText;
42071 c.addClassOnOver("close-over");
42072 c.on("click", this.closeClick, this);
42078 * Fires when this tab becomes the active tab.
42079 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42080 * @param {Roo.TabPanelItem} this
42084 * @event beforeclose
42085 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42086 * @param {Roo.TabPanelItem} this
42087 * @param {Object} e Set cancel to true on this object to cancel the close.
42089 "beforeclose": true,
42092 * Fires when this tab is closed.
42093 * @param {Roo.TabPanelItem} this
42097 * @event deactivate
42098 * Fires when this tab is no longer the active tab.
42099 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42100 * @param {Roo.TabPanelItem} this
42102 "deactivate" : true
42104 this.hidden = false;
42106 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42109 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42111 purgeListeners : function(){
42112 Roo.util.Observable.prototype.purgeListeners.call(this);
42113 this.el.removeAllListeners();
42116 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42119 this.status_node.addClass("active");
42122 this.tabPanel.stripWrap.repaint();
42124 this.fireEvent("activate", this.tabPanel, this);
42128 * Returns true if this tab is the active tab.
42129 * @return {Boolean}
42131 isActive : function(){
42132 return this.tabPanel.getActiveTab() == this;
42136 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42139 this.status_node.removeClass("active");
42141 this.fireEvent("deactivate", this.tabPanel, this);
42144 hideAction : function(){
42145 this.bodyEl.hide();
42146 this.bodyEl.setStyle("position", "absolute");
42147 this.bodyEl.setLeft("-20000px");
42148 this.bodyEl.setTop("-20000px");
42151 showAction : function(){
42152 this.bodyEl.setStyle("position", "relative");
42153 this.bodyEl.setTop("");
42154 this.bodyEl.setLeft("");
42155 this.bodyEl.show();
42159 * Set the tooltip for the tab.
42160 * @param {String} tooltip The tab's tooltip
42162 setTooltip : function(text){
42163 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42164 this.textEl.dom.qtip = text;
42165 this.textEl.dom.removeAttribute('title');
42167 this.textEl.dom.title = text;
42171 onTabClick : function(e){
42172 e.preventDefault();
42173 this.tabPanel.activate(this.id);
42176 onTabMouseDown : function(e){
42177 e.preventDefault();
42178 this.tabPanel.activate(this.id);
42181 getWidth : function(){
42182 return this.inner.getWidth();
42185 setWidth : function(width){
42186 var iwidth = width - this.linode.getPadding("lr");
42187 this.inner.setWidth(iwidth);
42188 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42189 this.linode.setWidth(width);
42193 * Show or hide the tab
42194 * @param {Boolean} hidden True to hide or false to show.
42196 setHidden : function(hidden){
42197 this.hidden = hidden;
42198 this.linode.setStyle("display", hidden ? "none" : "");
42202 * Returns true if this tab is "hidden"
42203 * @return {Boolean}
42205 isHidden : function(){
42206 return this.hidden;
42210 * Returns the text for this tab
42213 getText : function(){
42217 autoSize : function(){
42218 //this.el.beginMeasure();
42219 this.textEl.setWidth(1);
42221 * #2804 [new] Tabs in Roojs
42222 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42224 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42225 //this.el.endMeasure();
42229 * Sets the text for the tab (Note: this also sets the tooltip text)
42230 * @param {String} text The tab's text and tooltip
42232 setText : function(text){
42234 this.textEl.update(text);
42235 this.setTooltip(text);
42236 //if(!this.tabPanel.resizeTabs){
42237 // this.autoSize();
42241 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42243 activate : function(){
42244 this.tabPanel.activate(this.id);
42248 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42250 disable : function(){
42251 if(this.tabPanel.active != this){
42252 this.disabled = true;
42253 this.status_node.addClass("disabled");
42258 * Enables this TabPanelItem if it was previously disabled.
42260 enable : function(){
42261 this.disabled = false;
42262 this.status_node.removeClass("disabled");
42266 * Sets the content for this TabPanelItem.
42267 * @param {String} content The content
42268 * @param {Boolean} loadScripts true to look for and load scripts
42270 setContent : function(content, loadScripts){
42271 this.bodyEl.update(content, loadScripts);
42275 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42276 * @return {Roo.UpdateManager} The UpdateManager
42278 getUpdateManager : function(){
42279 return this.bodyEl.getUpdateManager();
42283 * Set a URL to be used to load the content for this TabPanelItem.
42284 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42285 * @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)
42286 * @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)
42287 * @return {Roo.UpdateManager} The UpdateManager
42289 setUrl : function(url, params, loadOnce){
42290 if(this.refreshDelegate){
42291 this.un('activate', this.refreshDelegate);
42293 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42294 this.on("activate", this.refreshDelegate);
42295 return this.bodyEl.getUpdateManager();
42299 _handleRefresh : function(url, params, loadOnce){
42300 if(!loadOnce || !this.loaded){
42301 var updater = this.bodyEl.getUpdateManager();
42302 updater.update(url, params, this._setLoaded.createDelegate(this));
42307 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42308 * Will fail silently if the setUrl method has not been called.
42309 * This does not activate the panel, just updates its content.
42311 refresh : function(){
42312 if(this.refreshDelegate){
42313 this.loaded = false;
42314 this.refreshDelegate();
42319 _setLoaded : function(){
42320 this.loaded = true;
42324 closeClick : function(e){
42327 this.fireEvent("beforeclose", this, o);
42328 if(o.cancel !== true){
42329 this.tabPanel.removeTab(this.id);
42333 * The text displayed in the tooltip for the close icon.
42336 closeText : "Close this tab"
42339 * This script refer to:
42340 * Title: International Telephone Input
42341 * Author: Jack O'Connor
42342 * Code version: v12.1.12
42343 * Availability: https://github.com/jackocnr/intl-tel-input.git
42346 Roo.bootstrap.PhoneInputData = function() {
42349 "Afghanistan (افغانستان)",
42354 "Albania (Shqipëri)",
42359 "Algeria (الجزائر)",
42384 "Antigua and Barbuda",
42394 "Armenia (Հայաստան)",
42410 "Austria (Österreich)",
42415 "Azerbaijan (Azərbaycan)",
42425 "Bahrain (البحرين)",
42430 "Bangladesh (বাংলাদেশ)",
42440 "Belarus (Беларусь)",
42445 "Belgium (België)",
42475 "Bosnia and Herzegovina (Босна и Херцеговина)",
42490 "British Indian Ocean Territory",
42495 "British Virgin Islands",
42505 "Bulgaria (България)",
42515 "Burundi (Uburundi)",
42520 "Cambodia (កម្ពុជា)",
42525 "Cameroon (Cameroun)",
42534 ["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"]
42537 "Cape Verde (Kabu Verdi)",
42542 "Caribbean Netherlands",
42553 "Central African Republic (République centrafricaine)",
42573 "Christmas Island",
42579 "Cocos (Keeling) Islands",
42590 "Comoros (جزر القمر)",
42595 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42600 "Congo (Republic) (Congo-Brazzaville)",
42620 "Croatia (Hrvatska)",
42641 "Czech Republic (Česká republika)",
42646 "Denmark (Danmark)",
42661 "Dominican Republic (República Dominicana)",
42665 ["809", "829", "849"]
42683 "Equatorial Guinea (Guinea Ecuatorial)",
42703 "Falkland Islands (Islas Malvinas)",
42708 "Faroe Islands (Føroyar)",
42729 "French Guiana (Guyane française)",
42734 "French Polynesia (Polynésie française)",
42749 "Georgia (საქართველო)",
42754 "Germany (Deutschland)",
42774 "Greenland (Kalaallit Nunaat)",
42811 "Guinea-Bissau (Guiné Bissau)",
42836 "Hungary (Magyarország)",
42841 "Iceland (Ísland)",
42861 "Iraq (العراق)",
42877 "Israel (ישראל)",
42904 "Jordan (الأردن)",
42909 "Kazakhstan (Казахстан)",
42930 "Kuwait (الكويت)",
42935 "Kyrgyzstan (Кыргызстан)",
42945 "Latvia (Latvija)",
42950 "Lebanon (لبنان)",
42965 "Libya (ليبيا)",
42975 "Lithuania (Lietuva)",
42990 "Macedonia (FYROM) (Македонија)",
42995 "Madagascar (Madagasikara)",
43025 "Marshall Islands",
43035 "Mauritania (موريتانيا)",
43040 "Mauritius (Moris)",
43061 "Moldova (Republica Moldova)",
43071 "Mongolia (Монгол)",
43076 "Montenegro (Crna Gora)",
43086 "Morocco (المغرب)",
43092 "Mozambique (Moçambique)",
43097 "Myanmar (Burma) (မြန်မာ)",
43102 "Namibia (Namibië)",
43117 "Netherlands (Nederland)",
43122 "New Caledonia (Nouvelle-Calédonie)",
43157 "North Korea (조선 민주주의 인민 공화국)",
43162 "Northern Mariana Islands",
43178 "Pakistan (پاکستان)",
43188 "Palestine (فلسطين)",
43198 "Papua New Guinea",
43240 "Réunion (La Réunion)",
43246 "Romania (România)",
43262 "Saint Barthélemy",
43273 "Saint Kitts and Nevis",
43283 "Saint Martin (Saint-Martin (partie française))",
43289 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43294 "Saint Vincent and the Grenadines",
43309 "São Tomé and Príncipe (São Tomé e Príncipe)",
43314 "Saudi Arabia (المملكة العربية السعودية)",
43319 "Senegal (Sénégal)",
43349 "Slovakia (Slovensko)",
43354 "Slovenia (Slovenija)",
43364 "Somalia (Soomaaliya)",
43374 "South Korea (대한민국)",
43379 "South Sudan (جنوب السودان)",
43389 "Sri Lanka (ශ්රී ලංකාව)",
43394 "Sudan (السودان)",
43404 "Svalbard and Jan Mayen",
43415 "Sweden (Sverige)",
43420 "Switzerland (Schweiz)",
43425 "Syria (سوريا)",
43470 "Trinidad and Tobago",
43475 "Tunisia (تونس)",
43480 "Turkey (Türkiye)",
43490 "Turks and Caicos Islands",
43500 "U.S. Virgin Islands",
43510 "Ukraine (Україна)",
43515 "United Arab Emirates (الإمارات العربية المتحدة)",
43537 "Uzbekistan (Oʻzbekiston)",
43547 "Vatican City (Città del Vaticano)",
43558 "Vietnam (Việt Nam)",
43563 "Wallis and Futuna (Wallis-et-Futuna)",
43568 "Western Sahara (الصحراء الغربية)",
43574 "Yemen (اليمن)",
43598 * This script refer to:
43599 * Title: International Telephone Input
43600 * Author: Jack O'Connor
43601 * Code version: v12.1.12
43602 * Availability: https://github.com/jackocnr/intl-tel-input.git
43606 * @class Roo.bootstrap.PhoneInput
43607 * @extends Roo.bootstrap.TriggerField
43608 * An input with International dial-code selection
43610 * @cfg {String} defaultDialCode default '+852'
43611 * @cfg {Array} preferedCountries default []
43614 * Create a new PhoneInput.
43615 * @param {Object} config Configuration options
43618 Roo.bootstrap.PhoneInput = function(config) {
43619 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43622 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43624 listWidth: undefined,
43626 selectedClass: 'active',
43628 invalidClass : "has-warning",
43630 validClass: 'has-success',
43632 allowed: '0123456789',
43637 * @cfg {String} defaultDialCode The default dial code when initializing the input
43639 defaultDialCode: '+852',
43642 * @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
43644 preferedCountries: false,
43646 getAutoCreate : function()
43648 var data = Roo.bootstrap.PhoneInputData();
43649 var align = this.labelAlign || this.parentLabelAlign();
43652 this.allCountries = [];
43653 this.dialCodeMapping = [];
43655 for (var i = 0; i < data.length; i++) {
43657 this.allCountries[i] = {
43661 priority: c[3] || 0,
43662 areaCodes: c[4] || null
43664 this.dialCodeMapping[c[2]] = {
43667 priority: c[3] || 0,
43668 areaCodes: c[4] || null
43680 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43681 maxlength: this.max_length,
43682 cls : 'form-control tel-input',
43683 autocomplete: 'new-password'
43686 var hiddenInput = {
43689 cls: 'hidden-tel-input'
43693 hiddenInput.name = this.name;
43696 if (this.disabled) {
43697 input.disabled = true;
43700 var flag_container = {
43717 cls: this.hasFeedback ? 'has-feedback' : '',
43723 cls: 'dial-code-holder',
43730 cls: 'roo-select2-container input-group',
43737 if (this.fieldLabel.length) {
43740 tooltip: 'This field is required'
43746 cls: 'control-label',
43752 html: this.fieldLabel
43755 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43761 if(this.indicatorpos == 'right') {
43762 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43769 if(align == 'left') {
43777 if(this.labelWidth > 12){
43778 label.style = "width: " + this.labelWidth + 'px';
43780 if(this.labelWidth < 13 && this.labelmd == 0){
43781 this.labelmd = this.labelWidth;
43783 if(this.labellg > 0){
43784 label.cls += ' col-lg-' + this.labellg;
43785 input.cls += ' col-lg-' + (12 - this.labellg);
43787 if(this.labelmd > 0){
43788 label.cls += ' col-md-' + this.labelmd;
43789 container.cls += ' col-md-' + (12 - this.labelmd);
43791 if(this.labelsm > 0){
43792 label.cls += ' col-sm-' + this.labelsm;
43793 container.cls += ' col-sm-' + (12 - this.labelsm);
43795 if(this.labelxs > 0){
43796 label.cls += ' col-xs-' + this.labelxs;
43797 container.cls += ' col-xs-' + (12 - this.labelxs);
43807 var settings = this;
43809 ['xs','sm','md','lg'].map(function(size){
43810 if (settings[size]) {
43811 cfg.cls += ' col-' + size + '-' + settings[size];
43815 this.store = new Roo.data.Store({
43816 proxy : new Roo.data.MemoryProxy({}),
43817 reader : new Roo.data.JsonReader({
43828 'name' : 'dialCode',
43832 'name' : 'priority',
43836 'name' : 'areaCodes',
43843 if(!this.preferedCountries) {
43844 this.preferedCountries = [
43851 var p = this.preferedCountries.reverse();
43854 for (var i = 0; i < p.length; i++) {
43855 for (var j = 0; j < this.allCountries.length; j++) {
43856 if(this.allCountries[j].iso2 == p[i]) {
43857 var t = this.allCountries[j];
43858 this.allCountries.splice(j,1);
43859 this.allCountries.unshift(t);
43865 this.store.proxy.data = {
43867 data: this.allCountries
43873 initEvents : function()
43876 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43878 this.indicator = this.indicatorEl();
43879 this.flag = this.flagEl();
43880 this.dialCodeHolder = this.dialCodeHolderEl();
43882 this.trigger = this.el.select('div.flag-box',true).first();
43883 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43888 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43889 _this.list.setWidth(lw);
43892 this.list.on('mouseover', this.onViewOver, this);
43893 this.list.on('mousemove', this.onViewMove, this);
43894 this.inputEl().on("keyup", this.onKeyUp, this);
43895 this.inputEl().on("keypress", this.onKeyPress, this);
43897 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43899 this.view = new Roo.View(this.list, this.tpl, {
43900 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43903 this.view.on('click', this.onViewClick, this);
43904 this.setValue(this.defaultDialCode);
43907 onTriggerClick : function(e)
43909 Roo.log('trigger click');
43914 if(this.isExpanded()){
43916 this.hasFocus = false;
43918 this.store.load({});
43919 this.hasFocus = true;
43924 isExpanded : function()
43926 return this.list.isVisible();
43929 collapse : function()
43931 if(!this.isExpanded()){
43935 Roo.get(document).un('mousedown', this.collapseIf, this);
43936 Roo.get(document).un('mousewheel', this.collapseIf, this);
43937 this.fireEvent('collapse', this);
43941 expand : function()
43945 if(this.isExpanded() || !this.hasFocus){
43949 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43950 this.list.setWidth(lw);
43953 this.restrictHeight();
43955 Roo.get(document).on('mousedown', this.collapseIf, this);
43956 Roo.get(document).on('mousewheel', this.collapseIf, this);
43958 this.fireEvent('expand', this);
43961 restrictHeight : function()
43963 this.list.alignTo(this.inputEl(), this.listAlign);
43964 this.list.alignTo(this.inputEl(), this.listAlign);
43967 onViewOver : function(e, t)
43969 if(this.inKeyMode){
43972 var item = this.view.findItemFromChild(t);
43975 var index = this.view.indexOf(item);
43976 this.select(index, false);
43981 onViewClick : function(view, doFocus, el, e)
43983 var index = this.view.getSelectedIndexes()[0];
43985 var r = this.store.getAt(index);
43988 this.onSelect(r, index);
43990 if(doFocus !== false && !this.blockFocus){
43991 this.inputEl().focus();
43995 onViewMove : function(e, t)
43997 this.inKeyMode = false;
44000 select : function(index, scrollIntoView)
44002 this.selectedIndex = index;
44003 this.view.select(index);
44004 if(scrollIntoView !== false){
44005 var el = this.view.getNode(index);
44007 this.list.scrollChildIntoView(el, false);
44012 createList : function()
44014 this.list = Roo.get(document.body).createChild({
44016 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44017 style: 'display:none'
44020 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44023 collapseIf : function(e)
44025 var in_combo = e.within(this.el);
44026 var in_list = e.within(this.list);
44027 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44029 if (in_combo || in_list || is_list) {
44035 onSelect : function(record, index)
44037 if(this.fireEvent('beforeselect', this, record, index) !== false){
44039 this.setFlagClass(record.data.iso2);
44040 this.setDialCode(record.data.dialCode);
44041 this.hasFocus = false;
44043 this.fireEvent('select', this, record, index);
44047 flagEl : function()
44049 var flag = this.el.select('div.flag',true).first();
44056 dialCodeHolderEl : function()
44058 var d = this.el.select('input.dial-code-holder',true).first();
44065 setDialCode : function(v)
44067 this.dialCodeHolder.dom.value = '+'+v;
44070 setFlagClass : function(n)
44072 this.flag.dom.className = 'flag '+n;
44075 getValue : function()
44077 var v = this.inputEl().getValue();
44078 if(this.dialCodeHolder) {
44079 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44084 setValue : function(v)
44086 var d = this.getDialCode(v);
44088 //invalid dial code
44089 if(v.length == 0 || !d || d.length == 0) {
44091 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44092 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44098 this.setFlagClass(this.dialCodeMapping[d].iso2);
44099 this.setDialCode(d);
44100 this.inputEl().dom.value = v.replace('+'+d,'');
44101 this.hiddenEl().dom.value = this.getValue();
44106 getDialCode : function(v)
44110 if (v.length == 0) {
44111 return this.dialCodeHolder.dom.value;
44115 if (v.charAt(0) != "+") {
44118 var numericChars = "";
44119 for (var i = 1; i < v.length; i++) {
44120 var c = v.charAt(i);
44123 if (this.dialCodeMapping[numericChars]) {
44124 dialCode = v.substr(1, i);
44126 if (numericChars.length == 4) {
44136 this.setValue(this.defaultDialCode);
44140 hiddenEl : function()
44142 return this.el.select('input.hidden-tel-input',true).first();
44145 // after setting val
44146 onKeyUp : function(e){
44147 this.setValue(this.getValue());
44150 onKeyPress : function(e){
44151 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44158 * @class Roo.bootstrap.MoneyField
44159 * @extends Roo.bootstrap.ComboBox
44160 * Bootstrap MoneyField class
44163 * Create a new MoneyField.
44164 * @param {Object} config Configuration options
44167 Roo.bootstrap.MoneyField = function(config) {
44169 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44173 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44176 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44178 allowDecimals : true,
44180 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44182 decimalSeparator : ".",
44184 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44186 decimalPrecision : 0,
44188 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44190 allowNegative : true,
44192 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44196 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44198 minValue : Number.NEGATIVE_INFINITY,
44200 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44202 maxValue : Number.MAX_VALUE,
44204 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44206 minText : "The minimum value for this field is {0}",
44208 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44210 maxText : "The maximum value for this field is {0}",
44212 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44213 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44215 nanText : "{0} is not a valid number",
44217 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44221 * @cfg {String} defaults currency of the MoneyField
44222 * value should be in lkey
44224 defaultCurrency : false,
44226 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44228 thousandsDelimiter : false,
44230 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44241 getAutoCreate : function()
44243 var align = this.labelAlign || this.parentLabelAlign();
44255 cls : 'form-control roo-money-amount-input',
44256 autocomplete: 'new-password'
44259 var hiddenInput = {
44263 cls: 'hidden-number-input'
44266 if(this.max_length) {
44267 input.maxlength = this.max_length;
44271 hiddenInput.name = this.name;
44274 if (this.disabled) {
44275 input.disabled = true;
44278 var clg = 12 - this.inputlg;
44279 var cmd = 12 - this.inputmd;
44280 var csm = 12 - this.inputsm;
44281 var cxs = 12 - this.inputxs;
44285 cls : 'row roo-money-field',
44289 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44293 cls: 'roo-select2-container input-group',
44297 cls : 'form-control roo-money-currency-input',
44298 autocomplete: 'new-password',
44300 name : this.currencyName
44304 cls : 'input-group-addon',
44318 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44322 cls: this.hasFeedback ? 'has-feedback' : '',
44333 if (this.fieldLabel.length) {
44336 tooltip: 'This field is required'
44342 cls: 'control-label',
44348 html: this.fieldLabel
44351 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44357 if(this.indicatorpos == 'right') {
44358 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44365 if(align == 'left') {
44373 if(this.labelWidth > 12){
44374 label.style = "width: " + this.labelWidth + 'px';
44376 if(this.labelWidth < 13 && this.labelmd == 0){
44377 this.labelmd = this.labelWidth;
44379 if(this.labellg > 0){
44380 label.cls += ' col-lg-' + this.labellg;
44381 input.cls += ' col-lg-' + (12 - this.labellg);
44383 if(this.labelmd > 0){
44384 label.cls += ' col-md-' + this.labelmd;
44385 container.cls += ' col-md-' + (12 - this.labelmd);
44387 if(this.labelsm > 0){
44388 label.cls += ' col-sm-' + this.labelsm;
44389 container.cls += ' col-sm-' + (12 - this.labelsm);
44391 if(this.labelxs > 0){
44392 label.cls += ' col-xs-' + this.labelxs;
44393 container.cls += ' col-xs-' + (12 - this.labelxs);
44404 var settings = this;
44406 ['xs','sm','md','lg'].map(function(size){
44407 if (settings[size]) {
44408 cfg.cls += ' col-' + size + '-' + settings[size];
44415 initEvents : function()
44417 this.indicator = this.indicatorEl();
44419 this.initCurrencyEvent();
44421 this.initNumberEvent();
44424 initCurrencyEvent : function()
44427 throw "can not find store for combo";
44430 this.store = Roo.factory(this.store, Roo.data);
44431 this.store.parent = this;
44435 this.triggerEl = this.el.select('.input-group-addon', true).first();
44437 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44442 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44443 _this.list.setWidth(lw);
44446 this.list.on('mouseover', this.onViewOver, this);
44447 this.list.on('mousemove', this.onViewMove, this);
44448 this.list.on('scroll', this.onViewScroll, this);
44451 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44454 this.view = new Roo.View(this.list, this.tpl, {
44455 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44458 this.view.on('click', this.onViewClick, this);
44460 this.store.on('beforeload', this.onBeforeLoad, this);
44461 this.store.on('load', this.onLoad, this);
44462 this.store.on('loadexception', this.onLoadException, this);
44464 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44465 "up" : function(e){
44466 this.inKeyMode = true;
44470 "down" : function(e){
44471 if(!this.isExpanded()){
44472 this.onTriggerClick();
44474 this.inKeyMode = true;
44479 "enter" : function(e){
44482 if(this.fireEvent("specialkey", this, e)){
44483 this.onViewClick(false);
44489 "esc" : function(e){
44493 "tab" : function(e){
44496 if(this.fireEvent("specialkey", this, e)){
44497 this.onViewClick(false);
44505 doRelay : function(foo, bar, hname){
44506 if(hname == 'down' || this.scope.isExpanded()){
44507 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44515 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44519 initNumberEvent : function(e)
44521 this.inputEl().on("keydown" , this.fireKey, this);
44522 this.inputEl().on("focus", this.onFocus, this);
44523 this.inputEl().on("blur", this.onBlur, this);
44525 this.inputEl().relayEvent('keyup', this);
44527 if(this.indicator){
44528 this.indicator.addClass('invisible');
44531 this.originalValue = this.getValue();
44533 if(this.validationEvent == 'keyup'){
44534 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44535 this.inputEl().on('keyup', this.filterValidation, this);
44537 else if(this.validationEvent !== false){
44538 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44541 if(this.selectOnFocus){
44542 this.on("focus", this.preFocus, this);
44545 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44546 this.inputEl().on("keypress", this.filterKeys, this);
44548 this.inputEl().relayEvent('keypress', this);
44551 var allowed = "0123456789";
44553 if(this.allowDecimals){
44554 allowed += this.decimalSeparator;
44557 if(this.allowNegative){
44561 if(this.thousandsDelimiter) {
44565 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44567 var keyPress = function(e){
44569 var k = e.getKey();
44571 var c = e.getCharCode();
44574 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44575 allowed.indexOf(String.fromCharCode(c)) === -1
44581 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44585 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44590 this.inputEl().on("keypress", keyPress, this);
44594 onTriggerClick : function(e)
44601 this.loadNext = false;
44603 if(this.isExpanded()){
44608 this.hasFocus = true;
44610 if(this.triggerAction == 'all') {
44611 this.doQuery(this.allQuery, true);
44615 this.doQuery(this.getRawValue());
44618 getCurrency : function()
44620 var v = this.currencyEl().getValue();
44625 restrictHeight : function()
44627 this.list.alignTo(this.currencyEl(), this.listAlign);
44628 this.list.alignTo(this.currencyEl(), this.listAlign);
44631 onViewClick : function(view, doFocus, el, e)
44633 var index = this.view.getSelectedIndexes()[0];
44635 var r = this.store.getAt(index);
44638 this.onSelect(r, index);
44642 onSelect : function(record, index){
44644 if(this.fireEvent('beforeselect', this, record, index) !== false){
44646 this.setFromCurrencyData(index > -1 ? record.data : false);
44650 this.fireEvent('select', this, record, index);
44654 setFromCurrencyData : function(o)
44658 this.lastCurrency = o;
44660 if (this.currencyField) {
44661 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44663 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44666 this.lastSelectionText = currency;
44668 //setting default currency
44669 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44670 this.setCurrency(this.defaultCurrency);
44674 this.setCurrency(currency);
44677 setFromData : function(o)
44681 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44683 this.setFromCurrencyData(c);
44688 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44690 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44693 this.setValue(value);
44697 setCurrency : function(v)
44699 this.currencyValue = v;
44702 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44707 setValue : function(v)
44709 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44715 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44717 this.inputEl().dom.value = (v == '') ? '' :
44718 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44720 if(!this.allowZero && v === '0') {
44721 this.hiddenEl().dom.value = '';
44722 this.inputEl().dom.value = '';
44729 getRawValue : function()
44731 var v = this.inputEl().getValue();
44736 getValue : function()
44738 return this.fixPrecision(this.parseValue(this.getRawValue()));
44741 parseValue : function(value)
44743 if(this.thousandsDelimiter) {
44745 r = new RegExp(",", "g");
44746 value = value.replace(r, "");
44749 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44750 return isNaN(value) ? '' : value;
44754 fixPrecision : function(value)
44756 if(this.thousandsDelimiter) {
44758 r = new RegExp(",", "g");
44759 value = value.replace(r, "");
44762 var nan = isNaN(value);
44764 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44765 return nan ? '' : value;
44767 return parseFloat(value).toFixed(this.decimalPrecision);
44770 decimalPrecisionFcn : function(v)
44772 return Math.floor(v);
44775 validateValue : function(value)
44777 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44781 var num = this.parseValue(value);
44784 this.markInvalid(String.format(this.nanText, value));
44788 if(num < this.minValue){
44789 this.markInvalid(String.format(this.minText, this.minValue));
44793 if(num > this.maxValue){
44794 this.markInvalid(String.format(this.maxText, this.maxValue));
44801 validate : function()
44803 if(this.disabled || this.allowBlank){
44808 var currency = this.getCurrency();
44810 if(this.validateValue(this.getRawValue()) && currency.length){
44815 this.markInvalid();
44819 getName: function()
44824 beforeBlur : function()
44830 var v = this.parseValue(this.getRawValue());
44837 onBlur : function()
44841 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44842 //this.el.removeClass(this.focusClass);
44845 this.hasFocus = false;
44847 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44851 var v = this.getValue();
44853 if(String(v) !== String(this.startValue)){
44854 this.fireEvent('change', this, v, this.startValue);
44857 this.fireEvent("blur", this);
44860 inputEl : function()
44862 return this.el.select('.roo-money-amount-input', true).first();
44865 currencyEl : function()
44867 return this.el.select('.roo-money-currency-input', true).first();
44870 hiddenEl : function()
44872 return this.el.select('input.hidden-number-input',true).first();
44876 * @class Roo.bootstrap.BezierSignature
44877 * @extends Roo.bootstrap.Component
44878 * Bootstrap BezierSignature class
44879 * This script refer to:
44880 * Title: Signature Pad
44882 * Availability: https://github.com/szimek/signature_pad
44885 * Create a new BezierSignature
44886 * @param {Object} config The config object
44889 Roo.bootstrap.BezierSignature = function(config){
44890 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44896 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44903 mouse_btn_down: true,
44906 * @cfg {int} canvas height
44908 canvas_height: '200px',
44911 * @cfg {float|function} Radius of a single dot.
44916 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44921 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44926 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44931 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44936 * @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.
44938 bg_color: 'rgba(0, 0, 0, 0)',
44941 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44943 dot_color: 'black',
44946 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44948 velocity_filter_weight: 0.7,
44951 * @cfg {function} Callback when stroke begin.
44956 * @cfg {function} Callback when stroke end.
44960 getAutoCreate : function()
44962 var cls = 'roo-signature column';
44965 cls += ' ' + this.cls;
44975 for(var i = 0; i < col_sizes.length; i++) {
44976 if(this[col_sizes[i]]) {
44977 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44987 cls: 'roo-signature-body',
44991 cls: 'roo-signature-body-canvas',
44992 height: this.canvas_height,
44993 width: this.canvas_width
45000 style: 'display: none'
45008 initEvents: function()
45010 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45012 var canvas = this.canvasEl();
45014 // mouse && touch event swapping...
45015 canvas.dom.style.touchAction = 'none';
45016 canvas.dom.style.msTouchAction = 'none';
45018 this.mouse_btn_down = false;
45019 canvas.on('mousedown', this._handleMouseDown, this);
45020 canvas.on('mousemove', this._handleMouseMove, this);
45021 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45023 if (window.PointerEvent) {
45024 canvas.on('pointerdown', this._handleMouseDown, this);
45025 canvas.on('pointermove', this._handleMouseMove, this);
45026 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45029 if ('ontouchstart' in window) {
45030 canvas.on('touchstart', this._handleTouchStart, this);
45031 canvas.on('touchmove', this._handleTouchMove, this);
45032 canvas.on('touchend', this._handleTouchEnd, this);
45035 Roo.EventManager.onWindowResize(this.resize, this, true);
45037 // file input event
45038 this.fileEl().on('change', this.uploadImage, this);
45045 resize: function(){
45047 var canvas = this.canvasEl().dom;
45048 var ctx = this.canvasElCtx();
45049 var img_data = false;
45051 if(canvas.width > 0) {
45052 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45054 // setting canvas width will clean img data
45057 var style = window.getComputedStyle ?
45058 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45060 var padding_left = parseInt(style.paddingLeft) || 0;
45061 var padding_right = parseInt(style.paddingRight) || 0;
45063 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45066 ctx.putImageData(img_data, 0, 0);
45070 _handleMouseDown: function(e)
45072 if (e.browserEvent.which === 1) {
45073 this.mouse_btn_down = true;
45074 this.strokeBegin(e);
45078 _handleMouseMove: function (e)
45080 if (this.mouse_btn_down) {
45081 this.strokeMoveUpdate(e);
45085 _handleMouseUp: function (e)
45087 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45088 this.mouse_btn_down = false;
45093 _handleTouchStart: function (e) {
45095 e.preventDefault();
45096 if (e.browserEvent.targetTouches.length === 1) {
45097 // var touch = e.browserEvent.changedTouches[0];
45098 // this.strokeBegin(touch);
45100 this.strokeBegin(e); // assume e catching the correct xy...
45104 _handleTouchMove: function (e) {
45105 e.preventDefault();
45106 // var touch = event.targetTouches[0];
45107 // _this._strokeMoveUpdate(touch);
45108 this.strokeMoveUpdate(e);
45111 _handleTouchEnd: function (e) {
45112 var wasCanvasTouched = e.target === this.canvasEl().dom;
45113 if (wasCanvasTouched) {
45114 e.preventDefault();
45115 // var touch = event.changedTouches[0];
45116 // _this._strokeEnd(touch);
45121 reset: function () {
45122 this._lastPoints = [];
45123 this._lastVelocity = 0;
45124 this._lastWidth = (this.min_width + this.max_width) / 2;
45125 this.canvasElCtx().fillStyle = this.dot_color;
45128 strokeMoveUpdate: function(e)
45130 this.strokeUpdate(e);
45132 if (this.throttle) {
45133 this.throttleStroke(this.strokeUpdate, this.throttle);
45136 this.strokeUpdate(e);
45140 strokeBegin: function(e)
45142 var newPointGroup = {
45143 color: this.dot_color,
45147 if (typeof this.onBegin === 'function') {
45151 this.curve_data.push(newPointGroup);
45153 this.strokeUpdate(e);
45156 strokeUpdate: function(e)
45158 var rect = this.canvasEl().dom.getBoundingClientRect();
45159 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45160 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45161 var lastPoints = lastPointGroup.points;
45162 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45163 var isLastPointTooClose = lastPoint
45164 ? point.distanceTo(lastPoint) <= this.min_distance
45166 var color = lastPointGroup.color;
45167 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45168 var curve = this.addPoint(point);
45170 this.drawDot({color: color, point: point});
45173 this.drawCurve({color: color, curve: curve});
45183 strokeEnd: function(e)
45185 this.strokeUpdate(e);
45186 if (typeof this.onEnd === 'function') {
45191 addPoint: function (point) {
45192 var _lastPoints = this._lastPoints;
45193 _lastPoints.push(point);
45194 if (_lastPoints.length > 2) {
45195 if (_lastPoints.length === 3) {
45196 _lastPoints.unshift(_lastPoints[0]);
45198 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45199 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45200 _lastPoints.shift();
45206 calculateCurveWidths: function (startPoint, endPoint) {
45207 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45208 (1 - this.velocity_filter_weight) * this._lastVelocity;
45210 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45213 start: this._lastWidth
45216 this._lastVelocity = velocity;
45217 this._lastWidth = newWidth;
45221 drawDot: function (_a) {
45222 var color = _a.color, point = _a.point;
45223 var ctx = this.canvasElCtx();
45224 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45226 this.drawCurveSegment(point.x, point.y, width);
45228 ctx.fillStyle = color;
45232 drawCurve: function (_a) {
45233 var color = _a.color, curve = _a.curve;
45234 var ctx = this.canvasElCtx();
45235 var widthDelta = curve.endWidth - curve.startWidth;
45236 var drawSteps = Math.floor(curve.length()) * 2;
45238 ctx.fillStyle = color;
45239 for (var i = 0; i < drawSteps; i += 1) {
45240 var t = i / drawSteps;
45246 var x = uuu * curve.startPoint.x;
45247 x += 3 * uu * t * curve.control1.x;
45248 x += 3 * u * tt * curve.control2.x;
45249 x += ttt * curve.endPoint.x;
45250 var y = uuu * curve.startPoint.y;
45251 y += 3 * uu * t * curve.control1.y;
45252 y += 3 * u * tt * curve.control2.y;
45253 y += ttt * curve.endPoint.y;
45254 var width = curve.startWidth + ttt * widthDelta;
45255 this.drawCurveSegment(x, y, width);
45261 drawCurveSegment: function (x, y, width) {
45262 var ctx = this.canvasElCtx();
45264 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45265 this.is_empty = false;
45270 var ctx = this.canvasElCtx();
45271 var canvas = this.canvasEl().dom;
45272 ctx.fillStyle = this.bg_color;
45273 ctx.clearRect(0, 0, canvas.width, canvas.height);
45274 ctx.fillRect(0, 0, canvas.width, canvas.height);
45275 this.curve_data = [];
45277 this.is_empty = true;
45282 return this.el.select('input',true).first();
45285 canvasEl: function()
45287 return this.el.select('canvas',true).first();
45290 canvasElCtx: function()
45292 return this.el.select('canvas',true).first().dom.getContext('2d');
45295 getImage: function(type)
45297 if(this.is_empty) {
45302 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45305 drawFromImage: function(img_src)
45307 var img = new Image();
45309 img.onload = function(){
45310 this.canvasElCtx().drawImage(img, 0, 0);
45315 this.is_empty = false;
45318 selectImage: function()
45320 this.fileEl().dom.click();
45323 uploadImage: function(e)
45325 var reader = new FileReader();
45327 reader.onload = function(e){
45328 var img = new Image();
45329 img.onload = function(){
45331 this.canvasElCtx().drawImage(img, 0, 0);
45333 img.src = e.target.result;
45336 reader.readAsDataURL(e.target.files[0]);
45339 // Bezier Point Constructor
45340 Point: (function () {
45341 function Point(x, y, time) {
45344 this.time = time || Date.now();
45346 Point.prototype.distanceTo = function (start) {
45347 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45349 Point.prototype.equals = function (other) {
45350 return this.x === other.x && this.y === other.y && this.time === other.time;
45352 Point.prototype.velocityFrom = function (start) {
45353 return this.time !== start.time
45354 ? this.distanceTo(start) / (this.time - start.time)
45361 // Bezier Constructor
45362 Bezier: (function () {
45363 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45364 this.startPoint = startPoint;
45365 this.control2 = control2;
45366 this.control1 = control1;
45367 this.endPoint = endPoint;
45368 this.startWidth = startWidth;
45369 this.endWidth = endWidth;
45371 Bezier.fromPoints = function (points, widths, scope) {
45372 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45373 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45374 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45376 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45377 var dx1 = s1.x - s2.x;
45378 var dy1 = s1.y - s2.y;
45379 var dx2 = s2.x - s3.x;
45380 var dy2 = s2.y - s3.y;
45381 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45382 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45383 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45384 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45385 var dxm = m1.x - m2.x;
45386 var dym = m1.y - m2.y;
45387 var k = l2 / (l1 + l2);
45388 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45389 var tx = s2.x - cm.x;
45390 var ty = s2.y - cm.y;
45392 c1: new scope.Point(m1.x + tx, m1.y + ty),
45393 c2: new scope.Point(m2.x + tx, m2.y + ty)
45396 Bezier.prototype.length = function () {
45401 for (var i = 0; i <= steps; i += 1) {
45403 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45404 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45406 var xdiff = cx - px;
45407 var ydiff = cy - py;
45408 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45415 Bezier.prototype.point = function (t, start, c1, c2, end) {
45416 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45417 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45418 + (3.0 * c2 * (1.0 - t) * t * t)
45419 + (end * t * t * t);
45424 throttleStroke: function(fn, wait) {
45425 if (wait === void 0) { wait = 250; }
45427 var timeout = null;
45431 var later = function () {
45432 previous = Date.now();
45434 result = fn.apply(storedContext, storedArgs);
45436 storedContext = null;
45440 return function wrapper() {
45442 for (var _i = 0; _i < arguments.length; _i++) {
45443 args[_i] = arguments[_i];
45445 var now = Date.now();
45446 var remaining = wait - (now - previous);
45447 storedContext = this;
45449 if (remaining <= 0 || remaining > wait) {
45451 clearTimeout(timeout);
45455 result = fn.apply(storedContext, storedArgs);
45457 storedContext = null;
45461 else if (!timeout) {
45462 timeout = window.setTimeout(later, remaining);