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
852 * Bootstrap Body class
856 * @param {Object} config The config object
859 Roo.bootstrap.Body = function(config){
861 config = config || {};
863 Roo.bootstrap.Body.superclass.constructor.call(this, config);
864 this.el = Roo.get(config.el ? config.el : document.body );
865 if (this.cls && this.cls.length) {
866 Roo.get(document.body).addClass(this.cls);
870 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
872 is_body : true,// just to make sure it's constructed?
877 onRender : function(ct, position)
879 /* Roo.log("Roo.bootstrap.Body - onRender");
880 if (this.cls && this.cls.length) {
881 Roo.get(document.body).addClass(this.cls);
900 * @class Roo.bootstrap.ButtonGroup
901 * @extends Roo.bootstrap.Component
902 * Bootstrap ButtonGroup class
903 * @cfg {String} size lg | sm | xs (default empty normal)
904 * @cfg {String} align vertical | justified (default none)
905 * @cfg {String} direction up | down (default down)
906 * @cfg {Boolean} toolbar false | true
907 * @cfg {Boolean} btn true | false
912 * @param {Object} config The config object
915 Roo.bootstrap.ButtonGroup = function(config){
916 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
919 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
927 getAutoCreate : function(){
933 cfg.html = this.html || cfg.html;
944 if (['vertical','justified'].indexOf(this.align)!==-1) {
945 cfg.cls = 'btn-group-' + this.align;
947 if (this.align == 'justified') {
948 console.log(this.items);
952 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
953 cfg.cls += ' btn-group-' + this.size;
956 if (this.direction == 'up') {
957 cfg.cls += ' dropup' ;
963 * Add a button to the group (similar to NavItem API.)
965 addItem : function(cfg)
967 var cn = new Roo.bootstrap.Button(cfg);
969 cn.parentId = this.id;
970 cn.onRender(this.el, null);
984 * @class Roo.bootstrap.Button
985 * @extends Roo.bootstrap.Component
986 * Bootstrap Button class
987 * @cfg {String} html The button content
988 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
989 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
990 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
991 * @cfg {String} size (lg|sm|xs)
992 * @cfg {String} tag (a|input|submit)
993 * @cfg {String} href empty or href
994 * @cfg {Boolean} disabled default false;
995 * @cfg {Boolean} isClose default false;
996 * @cfg {String} glyphicon depricated - use fa
997 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
998 * @cfg {String} badge text for badge
999 * @cfg {String} theme (default|glow)
1000 * @cfg {Boolean} inverse dark themed version
1001 * @cfg {Boolean} toggle is it a slidy toggle button
1002 * @cfg {Boolean} pressed default null - if the button ahs active state
1003 * @cfg {String} ontext text for on slidy toggle state
1004 * @cfg {String} offtext text for off slidy toggle state
1005 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1006 * @cfg {Boolean} removeClass remove the standard class..
1007 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1008 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1011 * Create a new button
1012 * @param {Object} config The config object
1016 Roo.bootstrap.Button = function(config){
1017 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1023 * When a button is pressed
1024 * @param {Roo.bootstrap.Button} btn
1025 * @param {Roo.EventObject} e
1030 * When a button is double clicked
1031 * @param {Roo.bootstrap.Button} btn
1032 * @param {Roo.EventObject} e
1037 * After the button has been toggles
1038 * @param {Roo.bootstrap.Button} btn
1039 * @param {Roo.EventObject} e
1040 * @param {boolean} pressed (also available as button.pressed)
1046 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1067 preventDefault: true,
1076 getAutoCreate : function(){
1084 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1085 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1086 this.tag = 'button';
1090 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1092 if (this.toggle == true) {
1095 cls: 'slider-frame roo-button',
1099 'data-on-text':'ON',
1100 'data-off-text':'OFF',
1101 cls: 'slider-button',
1106 // why are we validating the weights?
1107 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1108 cfg.cls += ' ' + this.weight;
1115 cfg.cls += ' close';
1117 cfg["aria-hidden"] = true;
1119 cfg.html = "×";
1125 if (this.theme==='default') {
1126 cfg.cls = 'btn roo-button';
1128 //if (this.parentType != 'Navbar') {
1129 this.weight = this.weight.length ? this.weight : 'default';
1131 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1133 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1134 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1135 cfg.cls += ' btn-' + outline + weight;
1136 if (this.weight == 'default') {
1138 cfg.cls += ' btn-' + this.weight;
1141 } else if (this.theme==='glow') {
1144 cfg.cls = 'btn-glow roo-button';
1146 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1148 cfg.cls += ' ' + this.weight;
1154 this.cls += ' inverse';
1158 if (this.active || this.pressed === true) {
1159 cfg.cls += ' active';
1162 if (this.disabled) {
1163 cfg.disabled = 'disabled';
1167 Roo.log('changing to ul' );
1169 this.glyphicon = 'caret';
1170 if (Roo.bootstrap.version == 4) {
1171 this.fa = 'caret-down';
1176 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1178 //gsRoo.log(this.parentType);
1179 if (this.parentType === 'Navbar' && !this.parent().bar) {
1180 Roo.log('changing to li?');
1189 href : this.href || '#'
1192 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1193 cfg.cls += ' dropdown';
1200 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1202 if (this.glyphicon) {
1203 cfg.html = ' ' + cfg.html;
1208 cls: 'glyphicon glyphicon-' + this.glyphicon
1213 cfg.html = ' ' + cfg.html;
1218 cls: 'fa fas fa-' + this.fa
1228 // cfg.cls='btn roo-button';
1232 var value = cfg.html;
1237 cls: 'glyphicon glyphicon-' + this.glyphicon,
1244 cls: 'fa fas fa-' + this.fa,
1249 var bw = this.badge_weight.length ? this.badge_weight :
1250 (this.weight.length ? this.weight : 'secondary');
1251 bw = bw == 'default' ? 'secondary' : bw;
1257 cls: 'badge badge-' + bw,
1266 cfg.cls += ' dropdown';
1267 cfg.html = typeof(cfg.html) != 'undefined' ?
1268 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1271 if (cfg.tag !== 'a' && this.href !== '') {
1272 throw "Tag must be a to set href.";
1273 } else if (this.href.length > 0) {
1274 cfg.href = this.href;
1277 if(this.removeClass){
1282 cfg.target = this.target;
1287 initEvents: function() {
1288 // Roo.log('init events?');
1289 // Roo.log(this.el.dom);
1292 if (typeof (this.menu) != 'undefined') {
1293 this.menu.parentType = this.xtype;
1294 this.menu.triggerEl = this.el;
1295 this.addxtype(Roo.apply({}, this.menu));
1299 if (this.el.hasClass('roo-button')) {
1300 this.el.on('click', this.onClick, this);
1301 this.el.on('dblclick', this.onDblClick, this);
1303 this.el.select('.roo-button').on('click', this.onClick, this);
1304 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1308 if(this.removeClass){
1309 this.el.on('click', this.onClick, this);
1312 if (this.group === true) {
1313 if (this.pressed === false || this.pressed === true) {
1316 this.pressed = false;
1317 this.setActive(this.pressed);
1322 this.el.enableDisplayMode();
1325 onClick : function(e)
1327 if (this.disabled) {
1331 Roo.log('button on click ');
1332 if(this.preventDefault){
1341 this.setActive(true);
1342 var pi = this.parent().items;
1343 for (var i = 0;i < pi.length;i++) {
1344 if (this == pi[i]) {
1347 if (pi[i].el.hasClass('roo-button')) {
1348 pi[i].setActive(false);
1351 this.fireEvent('click', this, e);
1355 if (this.pressed === true || this.pressed === false) {
1356 this.toggleActive(e);
1360 this.fireEvent('click', this, e);
1362 onDblClick: function(e)
1364 if (this.disabled) {
1367 if(this.preventDefault){
1370 this.fireEvent('dblclick', this, e);
1373 * Enables this button
1377 this.disabled = false;
1378 this.el.removeClass('disabled');
1379 this.el.dom.removeAttribute("disabled");
1383 * Disable this button
1385 disable : function()
1387 this.disabled = true;
1388 this.el.addClass('disabled');
1389 this.el.attr("disabled", "disabled")
1392 * sets the active state on/off,
1393 * @param {Boolean} state (optional) Force a particular state
1395 setActive : function(v) {
1397 this.el[v ? 'addClass' : 'removeClass']('active');
1401 * toggles the current active state
1403 toggleActive : function(e)
1405 this.setActive(!this.pressed); // this modifies pressed...
1406 this.fireEvent('toggle', this, e, this.pressed);
1409 * get the current active state
1410 * @return {boolean} true if it's active
1412 isActive : function()
1414 return this.el.hasClass('active');
1417 * set the text of the first selected button
1419 setText : function(str)
1421 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1424 * get the text of the first selected button
1426 getText : function()
1428 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1431 setWeight : function(str)
1433 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1434 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1436 var outline = this.outline ? 'outline-' : '';
1437 if (str == 'default') {
1438 this.el.addClass('btn-default btn-outline-secondary');
1441 this.el.addClass('btn-' + outline + str);
1446 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1448 Roo.bootstrap.Button.weights = [
1468 * @class Roo.bootstrap.Column
1469 * @extends Roo.bootstrap.Component
1470 * Bootstrap Column class
1471 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1472 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1473 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1474 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1475 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1476 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1477 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1478 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1481 * @cfg {Boolean} hidden (true|false) hide the element
1482 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1483 * @cfg {String} fa (ban|check|...) font awesome icon
1484 * @cfg {Number} fasize (1|2|....) font awsome size
1486 * @cfg {String} icon (info-sign|check|...) glyphicon name
1488 * @cfg {String} html content of column.
1491 * Create a new Column
1492 * @param {Object} config The config object
1495 Roo.bootstrap.Column = function(config){
1496 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1499 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1517 getAutoCreate : function(){
1518 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1526 var sizes = ['xs','sm','md','lg'];
1527 sizes.map(function(size ,ix){
1528 //Roo.log( size + ':' + settings[size]);
1530 if (settings[size+'off'] !== false) {
1531 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1534 if (settings[size] === false) {
1538 if (!settings[size]) { // 0 = hidden
1539 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1541 for (var i = ix; i > -1; i--) {
1542 cfg.cls += ' d-' + sizes[i] + '-none';
1548 cfg.cls += ' col-' + size + '-' + settings[size] + (
1549 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1555 cfg.cls += ' hidden';
1558 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1559 cfg.cls +=' alert alert-' + this.alert;
1563 if (this.html.length) {
1564 cfg.html = this.html;
1568 if (this.fasize > 1) {
1569 fasize = ' fa-' + this.fasize + 'x';
1571 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1576 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1595 * @class Roo.bootstrap.Container
1596 * @extends Roo.bootstrap.Component
1597 * Bootstrap Container class
1598 * @cfg {Boolean} jumbotron is it a jumbotron element
1599 * @cfg {String} html content of element
1600 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1601 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1602 * @cfg {String} header content of header (for panel)
1603 * @cfg {String} footer content of footer (for panel)
1604 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1605 * @cfg {String} tag (header|aside|section) type of HTML tag.
1606 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1607 * @cfg {String} fa font awesome icon
1608 * @cfg {String} icon (info-sign|check|...) glyphicon name
1609 * @cfg {Boolean} hidden (true|false) hide the element
1610 * @cfg {Boolean} expandable (true|false) default false
1611 * @cfg {Boolean} expanded (true|false) default true
1612 * @cfg {String} rheader contet on the right of header
1613 * @cfg {Boolean} clickable (true|false) default false
1617 * Create a new Container
1618 * @param {Object} config The config object
1621 Roo.bootstrap.Container = function(config){
1622 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1628 * After the panel has been expand
1630 * @param {Roo.bootstrap.Container} this
1635 * After the panel has been collapsed
1637 * @param {Roo.bootstrap.Container} this
1642 * When a element is chick
1643 * @param {Roo.bootstrap.Container} this
1644 * @param {Roo.EventObject} e
1650 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1668 getChildContainer : function() {
1674 if (this.panel.length) {
1675 return this.el.select('.panel-body',true).first();
1682 getAutoCreate : function(){
1685 tag : this.tag || 'div',
1689 if (this.jumbotron) {
1690 cfg.cls = 'jumbotron';
1695 // - this is applied by the parent..
1697 // cfg.cls = this.cls + '';
1700 if (this.sticky.length) {
1702 var bd = Roo.get(document.body);
1703 if (!bd.hasClass('bootstrap-sticky')) {
1704 bd.addClass('bootstrap-sticky');
1705 Roo.select('html',true).setStyle('height', '100%');
1708 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1712 if (this.well.length) {
1713 switch (this.well) {
1716 cfg.cls +=' well well-' +this.well;
1725 cfg.cls += ' hidden';
1729 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1730 cfg.cls +=' alert alert-' + this.alert;
1735 if (this.panel.length) {
1736 cfg.cls += ' panel panel-' + this.panel;
1738 if (this.header.length) {
1742 if(this.expandable){
1744 cfg.cls = cfg.cls + ' expandable';
1748 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1756 cls : 'panel-title',
1757 html : (this.expandable ? ' ' : '') + this.header
1761 cls: 'panel-header-right',
1767 cls : 'panel-heading',
1768 style : this.expandable ? 'cursor: pointer' : '',
1776 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1781 if (this.footer.length) {
1783 cls : 'panel-footer',
1792 body.html = this.html || cfg.html;
1793 // prefix with the icons..
1795 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1798 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1803 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1804 cfg.cls = 'container';
1810 initEvents: function()
1812 if(this.expandable){
1813 var headerEl = this.headerEl();
1816 headerEl.on('click', this.onToggleClick, this);
1821 this.el.on('click', this.onClick, this);
1826 onToggleClick : function()
1828 var headerEl = this.headerEl();
1844 if(this.fireEvent('expand', this)) {
1846 this.expanded = true;
1848 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1850 this.el.select('.panel-body',true).first().removeClass('hide');
1852 var toggleEl = this.toggleEl();
1858 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1863 collapse : function()
1865 if(this.fireEvent('collapse', this)) {
1867 this.expanded = false;
1869 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1870 this.el.select('.panel-body',true).first().addClass('hide');
1872 var toggleEl = this.toggleEl();
1878 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1882 toggleEl : function()
1884 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1888 return this.el.select('.panel-heading .fa',true).first();
1891 headerEl : function()
1893 if(!this.el || !this.panel.length || !this.header.length){
1897 return this.el.select('.panel-heading',true).first()
1902 if(!this.el || !this.panel.length){
1906 return this.el.select('.panel-body',true).first()
1909 titleEl : function()
1911 if(!this.el || !this.panel.length || !this.header.length){
1915 return this.el.select('.panel-title',true).first();
1918 setTitle : function(v)
1920 var titleEl = this.titleEl();
1926 titleEl.dom.innerHTML = v;
1929 getTitle : function()
1932 var titleEl = this.titleEl();
1938 return titleEl.dom.innerHTML;
1941 setRightTitle : function(v)
1943 var t = this.el.select('.panel-header-right',true).first();
1949 t.dom.innerHTML = v;
1952 onClick : function(e)
1956 this.fireEvent('click', this, e);
1963 * This is BS4's Card element.. - similar to our containers probably..
1967 * @class Roo.bootstrap.Card
1968 * @extends Roo.bootstrap.Component
1969 * Bootstrap Card class
1972 * possible... may not be implemented..
1973 * @cfg {String} header_image src url of image.
1974 * @cfg {String|Object} header
1975 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1976 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1978 * @cfg {String} title
1979 * @cfg {String} subtitle
1980 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1981 * @cfg {String} footer
1983 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1985 * @cfg {String} margin (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1990 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1991 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1993 * @cfg {String} padding (0|1|2|3|4|5)
1994 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1995 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1996 * @cfg {String} padding_left (0|1|2|3|4|5)
1997 * @cfg {String} padding_right (0|1|2|3|4|5)
1998 * @cfg {String} padding_x (0|1|2|3|4|5)
1999 * @cfg {String} padding_y (0|1|2|3|4|5)
2001 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2007 * @config {Boolean} dragable if this card can be dragged.
2008 * @config {String} drag_group group for drag
2009 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2010 * @config {String} drop_group group for drag
2012 * @config {Boolean} collapsable can the body be collapsed.
2013 * @config {Boolean} collapsed is the body collapsed when rendered...
2014 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2015 * @config {Boolean} rotated is the body rotated when rendered...
2018 * Create a new Container
2019 * @param {Object} config The config object
2022 Roo.bootstrap.Card = function(config){
2023 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2029 * When a element a card is dropped
2030 * @param {Roo.bootstrap.Card} this
2033 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2034 * @param {String} position 'above' or 'below'
2035 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2041 * When a element a card is rotate
2042 * @param {Roo.bootstrap.Card} this
2043 * @param {Roo.Element} n the node being dropped?
2044 * @param {Boolean} rotate status
2049 * When a card element is dragged over ready to drop (return false to block dropable)
2050 * @param {Roo.bootstrap.Card} this
2051 * @param {Object} data from dragdrop
2059 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2064 margin: '', /// may be better in component?
2094 collapsable : false,
2103 childContainer : false,
2104 dropEl : false, /// the dom placeholde element that indicates drop location.
2105 containerEl: false, // body container
2106 bodyEl: false, // card-body
2107 headerContainerEl : false, //
2109 header_imageEl : false,
2112 layoutCls : function()
2116 Roo.log(this.margin_bottom.length);
2117 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2118 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2120 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2121 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2123 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2124 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2128 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2129 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2130 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2134 // more generic support?
2142 // Roo.log("Call onRender: " + this.xtype);
2143 /* We are looking at something like this.
2145 <img src="..." class="card-img-top" alt="...">
2146 <div class="card-body">
2147 <h5 class="card-title">Card title</h5>
2148 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2150 >> this bit is really the body...
2151 <div> << we will ad dthis in hopefully it will not break shit.
2153 ** card text does not actually have any styling...
2155 <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>
2158 <a href="#" class="card-link">Card link</a>
2161 <div class="card-footer">
2162 <small class="text-muted">Last updated 3 mins ago</small>
2166 getAutoCreate : function(){
2174 if (this.weight.length && this.weight != 'light') {
2175 cfg.cls += ' text-white';
2177 cfg.cls += ' text-dark'; // need as it's nested..
2179 if (this.weight.length) {
2180 cfg.cls += ' bg-' + this.weight;
2183 cfg.cls += ' ' + this.layoutCls();
2186 var hdr_ctr = false;
2187 if (this.header.length) {
2189 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2190 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2198 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2204 if (this.collapsable) {
2207 cls : 'd-block user-select-none',
2211 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2216 hdr.cn.push(hdr_ctr);
2221 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2226 if (this.header_image.length) {
2229 cls : 'card-img-top',
2230 src: this.header_image // escape?
2235 cls : 'card-img-top d-none'
2241 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2245 if (this.collapsable || this.rotateable) {
2248 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2255 if (this.title.length) {
2259 src: this.title // escape?
2263 if (this.subtitle.length) {
2267 src: this.subtitle // escape?
2273 cls : 'roo-card-body-ctr'
2276 if (this.html.length) {
2282 // fixme ? handle objects?
2284 if (this.footer.length) {
2287 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2292 cfg.cn.push({cls : 'card-footer d-none'});
2301 getCardHeader : function()
2303 var ret = this.el.select('.card-header',true).first();
2304 if (ret.hasClass('d-none')) {
2305 ret.removeClass('d-none');
2310 getCardFooter : function()
2312 var ret = this.el.select('.card-footer',true).first();
2313 if (ret.hasClass('d-none')) {
2314 ret.removeClass('d-none');
2319 getCardImageTop : function()
2321 var ret = this.header_imageEl;
2322 if (ret.hasClass('d-none')) {
2323 ret.removeClass('d-none');
2329 getChildContainer : function()
2335 return this.el.select('.roo-card-body-ctr',true).first();
2338 initEvents: function()
2340 this.bodyEl = this.el.select('.card-body',true).first();
2341 this.containerEl = this.getChildContainer();
2343 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2344 containerScroll: true,
2345 ddGroup: this.drag_group || 'default_card_drag_group'
2347 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2349 if (this.dropable) {
2350 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2351 containerScroll: true,
2352 ddGroup: this.drop_group || 'default_card_drag_group'
2354 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2355 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2356 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2357 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2358 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2361 if (this.collapsable) {
2362 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2364 if (this.rotateable) {
2365 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2367 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2369 this.footerEl = this.el.select('.card-footer',true).first();
2370 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2371 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2372 this.headerEl = this.el.select('.card-header',true).first();
2375 this.el.addClass('roo-card-rotated');
2376 this.fireEvent('rotate', this, true);
2378 this.header_imageEl = this.el.select('.card-img-top',true).first();
2379 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2382 getDragData : function(e)
2384 var target = this.getEl();
2386 //this.handleSelection(e);
2391 nodes: this.getEl(),
2396 dragData.ddel = target.dom ; // the div element
2397 Roo.log(target.getWidth( ));
2398 dragData.ddel.style.width = target.getWidth() + 'px';
2405 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2406 * whole Element becomes the target, and this causes the drop gesture to append.
2408 * Returns an object:
2411 position : 'below' or 'above'
2412 card : relateive to card OBJECT (or true for no cards listed)
2413 items_n : relative to nth item in list
2414 card_n : relative to nth card in list
2419 getTargetFromEvent : function(e, dragged_card_el)
2421 var target = e.getTarget();
2422 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2423 target = target.parentNode;
2434 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2435 // see if target is one of the 'cards'...
2438 //Roo.log(this.items.length);
2441 var last_card_n = 0;
2443 for (var i = 0;i< this.items.length;i++) {
2445 if (!this.items[i].el.hasClass('card')) {
2448 pos = this.getDropPoint(e, this.items[i].el.dom);
2450 cards_len = ret.cards.length;
2451 //Roo.log(this.items[i].el.dom.id);
2452 ret.cards.push(this.items[i]);
2454 if (ret.card_n < 0 && pos == 'above') {
2455 ret.position = cards_len > 0 ? 'below' : pos;
2456 ret.items_n = i > 0 ? i - 1 : 0;
2457 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2458 ret.card = ret.cards[ret.card_n];
2461 if (!ret.cards.length) {
2463 ret.position = 'below';
2467 // could not find a card.. stick it at the end..
2468 if (ret.card_n < 0) {
2469 ret.card_n = last_card_n;
2470 ret.card = ret.cards[last_card_n];
2471 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2472 ret.position = 'below';
2475 if (this.items[ret.items_n].el == dragged_card_el) {
2479 if (ret.position == 'below') {
2480 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2482 if (card_after && card_after.el == dragged_card_el) {
2489 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2491 if (card_before && card_before.el == dragged_card_el) {
2498 onNodeEnter : function(n, dd, e, data){
2501 onNodeOver : function(n, dd, e, data)
2504 var target_info = this.getTargetFromEvent(e,data.source.el);
2505 if (target_info === false) {
2506 this.dropPlaceHolder('hide');
2509 Roo.log(['getTargetFromEvent', target_info ]);
2512 if (this.fireEvent('cardover', this, [ data ]) === false) {
2516 this.dropPlaceHolder('show', target_info,data);
2520 onNodeOut : function(n, dd, e, data){
2521 this.dropPlaceHolder('hide');
2524 onNodeDrop : function(n, dd, e, data)
2527 // call drop - return false if
2529 // this could actually fail - if the Network drops..
2530 // we will ignore this at present..- client should probably reload
2531 // the whole set of cards if stuff like that fails.
2534 var info = this.getTargetFromEvent(e,data.source.el);
2535 if (info === false) {
2538 this.dropPlaceHolder('hide');
2542 this.acceptCard(data.source, info.position, info.card, info.items_n);
2546 firstChildCard : function()
2548 for (var i = 0;i< this.items.length;i++) {
2550 if (!this.items[i].el.hasClass('card')) {
2553 return this.items[i];
2555 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2560 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2562 acceptCard : function(move_card, position, next_to_card )
2564 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2568 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2570 move_card.parent().removeCard(move_card);
2573 var dom = move_card.el.dom;
2574 dom.style.width = ''; // clear with - which is set by drag.
2576 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2577 var cardel = next_to_card.el.dom;
2579 if (position == 'above' ) {
2580 cardel.parentNode.insertBefore(dom, cardel);
2581 } else if (cardel.nextSibling) {
2582 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2584 cardel.parentNode.append(dom);
2587 // card container???
2588 this.containerEl.dom.append(dom);
2591 //FIXME HANDLE card = true
2593 // add this to the correct place in items.
2595 // remove Card from items.
2598 if (this.items.length) {
2600 //Roo.log([info.items_n, info.position, this.items.length]);
2601 for (var i =0; i < this.items.length; i++) {
2602 if (i == to_items_n && position == 'above') {
2603 nitems.push(move_card);
2605 nitems.push(this.items[i]);
2606 if (i == to_items_n && position == 'below') {
2607 nitems.push(move_card);
2610 this.items = nitems;
2611 Roo.log(this.items);
2613 this.items.push(move_card);
2616 move_card.parentId = this.id;
2622 removeCard : function(c)
2624 this.items = this.items.filter(function(e) { return e != c });
2627 dom.parentNode.removeChild(dom);
2628 dom.style.width = ''; // clear with - which is set by drag.
2633 /** Decide whether to drop above or below a View node. */
2634 getDropPoint : function(e, n, dd)
2639 if (n == this.containerEl.dom) {
2642 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2643 var c = t + (b - t) / 2;
2644 var y = Roo.lib.Event.getPageY(e);
2651 onToggleCollapse : function(e)
2653 if (this.collapsed) {
2654 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2655 this.collapsableEl.addClass('show');
2656 this.collapsed = false;
2659 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2660 this.collapsableEl.removeClass('show');
2661 this.collapsed = true;
2666 onToggleRotate : function(e)
2668 this.collapsableEl.removeClass('show');
2669 this.footerEl.removeClass('d-none');
2670 this.el.removeClass('roo-card-rotated');
2671 this.el.removeClass('d-none');
2674 this.collapsableEl.addClass('show');
2675 this.rotated = false;
2676 this.fireEvent('rotate', this, this.rotated);
2679 this.el.addClass('roo-card-rotated');
2680 this.footerEl.addClass('d-none');
2681 this.el.select('.roo-collapsable').removeClass('show');
2683 this.rotated = true;
2684 this.fireEvent('rotate', this, this.rotated);
2688 dropPlaceHolder: function (action, info, data)
2690 if (this.dropEl === false) {
2691 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2695 this.dropEl.removeClass(['d-none', 'd-block']);
2696 if (action == 'hide') {
2698 this.dropEl.addClass('d-none');
2701 // FIXME - info.card == true!!!
2702 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2704 if (info.card !== true) {
2705 var cardel = info.card.el.dom;
2707 if (info.position == 'above') {
2708 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2709 } else if (cardel.nextSibling) {
2710 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2712 cardel.parentNode.append(this.dropEl.dom);
2715 // card container???
2716 this.containerEl.dom.append(this.dropEl.dom);
2719 this.dropEl.addClass('d-block roo-card-dropzone');
2721 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2728 setHeaderText: function(html)
2731 if (this.headerContainerEl) {
2732 this.headerContainerEl.dom.innerHTML = html;
2735 onHeaderImageLoad : function(ev, he)
2737 if (!this.header_image_fit_square) {
2741 var hw = he.naturalHeight / he.naturalWidth;
2744 //var w = he.dom.naturalWidth;
2747 he.style.position = 'relative';
2749 var nw = (ww * (1/hw));
2750 Roo.get(he).setSize( ww * (1/hw), ww);
2751 he.style.left = ((ww - nw)/ 2) + 'px';
2752 he.style.position = 'relative';
2763 * Card header - holder for the card header elements.
2768 * @class Roo.bootstrap.CardHeader
2769 * @extends Roo.bootstrap.Element
2770 * Bootstrap CardHeader class
2772 * Create a new Card Header - that you can embed children into
2773 * @param {Object} config The config object
2776 Roo.bootstrap.CardHeader = function(config){
2777 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2780 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2783 container_method : 'getCardHeader'
2796 * Card footer - holder for the card footer elements.
2801 * @class Roo.bootstrap.CardFooter
2802 * @extends Roo.bootstrap.Element
2803 * Bootstrap CardFooter class
2805 * Create a new Card Footer - that you can embed children into
2806 * @param {Object} config The config object
2809 Roo.bootstrap.CardFooter = function(config){
2810 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2813 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2816 container_method : 'getCardFooter'
2829 * Card header - holder for the card header elements.
2834 * @class Roo.bootstrap.CardImageTop
2835 * @extends Roo.bootstrap.Element
2836 * Bootstrap CardImageTop class
2838 * Create a new Card Image Top container
2839 * @param {Object} config The config object
2842 Roo.bootstrap.CardImageTop = function(config){
2843 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2846 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2849 container_method : 'getCardImageTop'
2864 * @class Roo.bootstrap.ButtonUploader
2865 * @extends Roo.bootstrap.Button
2866 * Bootstrap Button Uploader class - it's a button which when you add files to it
2869 * @cfg {Number} errorTimeout default 3000
2870 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2871 * @cfg {Array} html The button text.
2872 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2875 * Create a new CardUploader
2876 * @param {Object} config The config object
2879 Roo.bootstrap.ButtonUploader = function(config){
2883 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2889 * @event beforeselect
2890 * When button is pressed, before show upload files dialog is shown
2891 * @param {Roo.bootstrap.UploaderButton} this
2894 'beforeselect' : true,
2896 * @event fired when files have been selected,
2897 * When a the download link is clicked
2898 * @param {Roo.bootstrap.UploaderButton} this
2899 * @param {Array} Array of files that have been uploaded
2906 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2909 errorTimeout : 3000,
2913 fileCollection : false,
2918 getAutoCreate : function()
2923 cls : 'd-none roo-card-upload-selector'
2926 if (this.multiple) {
2927 im.multiple = 'multiple';
2933 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2943 initEvents : function()
2946 Roo.bootstrap.Button.prototype.initEvents.call(this);
2952 this.urlAPI = (window.createObjectURL && window) ||
2953 (window.URL && URL.revokeObjectURL && URL) ||
2954 (window.webkitURL && webkitURL);
2959 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2961 this.selectorEl.on('change', this.onFileSelected, this);
2968 onClick : function(e)
2972 if ( this.fireEvent('beforeselect', this) === false) {
2976 this.selectorEl.dom.click();
2980 onFileSelected : function(e)
2984 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2987 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2988 this.selectorEl.dom.value = '';// hopefully reset..
2990 this.fireEvent('uploaded', this, files );
2998 * addCard - add an Attachment to the uploader
2999 * @param data - the data about the image to upload
3003 title : "Title of file",
3004 is_uploaded : false,
3005 src : "http://.....",
3006 srcfile : { the File upload object },
3007 mimetype : file.type,
3010 .. any other data...
3035 * @class Roo.bootstrap.Img
3036 * @extends Roo.bootstrap.Component
3037 * Bootstrap Img class
3038 * @cfg {Boolean} imgResponsive false | true
3039 * @cfg {String} border rounded | circle | thumbnail
3040 * @cfg {String} src image source
3041 * @cfg {String} alt image alternative text
3042 * @cfg {String} href a tag href
3043 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3044 * @cfg {String} xsUrl xs image source
3045 * @cfg {String} smUrl sm image source
3046 * @cfg {String} mdUrl md image source
3047 * @cfg {String} lgUrl lg image source
3048 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3051 * Create a new Input
3052 * @param {Object} config The config object
3055 Roo.bootstrap.Img = function(config){
3056 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3062 * The img click event for the img.
3063 * @param {Roo.EventObject} e
3068 * The when any image loads
3069 * @param {Roo.EventObject} e
3075 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3077 imgResponsive: true,
3086 backgroundContain : false,
3088 getAutoCreate : function()
3090 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3091 return this.createSingleImg();
3096 cls: 'roo-image-responsive-group',
3101 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3103 if(!_this[size + 'Url']){
3109 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3110 html: _this.html || cfg.html,
3111 src: _this[size + 'Url']
3114 img.cls += ' roo-image-responsive-' + size;
3116 var s = ['xs', 'sm', 'md', 'lg'];
3118 s.splice(s.indexOf(size), 1);
3120 Roo.each(s, function(ss){
3121 img.cls += ' hidden-' + ss;
3124 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3125 cfg.cls += ' img-' + _this.border;
3129 cfg.alt = _this.alt;
3142 a.target = _this.target;
3146 cfg.cn.push((_this.href) ? a : img);
3153 createSingleImg : function()
3157 cls: (this.imgResponsive) ? 'img-responsive' : '',
3159 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3162 if (this.backgroundContain) {
3163 cfg.cls += ' background-contain';
3166 cfg.html = this.html || cfg.html;
3168 if (this.backgroundContain) {
3169 cfg.style="background-image: url(" + this.src + ')';
3171 cfg.src = this.src || cfg.src;
3174 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3175 cfg.cls += ' img-' + this.border;
3192 a.target = this.target;
3197 return (this.href) ? a : cfg;
3200 initEvents: function()
3203 this.el.on('click', this.onClick, this);
3205 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3206 this.el.on('load', this.onImageLoad, this);
3208 // not sure if this works.. not tested
3209 this.el.select('img', true).on('load', this.onImageLoad, this);
3214 onClick : function(e)
3216 Roo.log('img onclick');
3217 this.fireEvent('click', this, e);
3219 onImageLoad: function(e)
3221 Roo.log('img load');
3222 this.fireEvent('load', this, e);
3226 * Sets the url of the image - used to update it
3227 * @param {String} url the url of the image
3230 setSrc : function(url)
3234 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3235 if (this.backgroundContain) {
3236 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3238 this.el.dom.src = url;
3243 this.el.select('img', true).first().dom.src = url;
3259 * @class Roo.bootstrap.Link
3260 * @extends Roo.bootstrap.Component
3261 * Bootstrap Link Class
3262 * @cfg {String} alt image alternative text
3263 * @cfg {String} href a tag href
3264 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3265 * @cfg {String} html the content of the link.
3266 * @cfg {String} anchor name for the anchor link
3267 * @cfg {String} fa - favicon
3269 * @cfg {Boolean} preventDefault (true | false) default false
3273 * Create a new Input
3274 * @param {Object} config The config object
3277 Roo.bootstrap.Link = function(config){
3278 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3284 * The img click event for the img.
3285 * @param {Roo.EventObject} e
3291 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3295 preventDefault: false,
3301 getAutoCreate : function()
3303 var html = this.html || '';
3305 if (this.fa !== false) {
3306 html = '<i class="fa fa-' + this.fa + '"></i>';
3311 // anchor's do not require html/href...
3312 if (this.anchor === false) {
3314 cfg.href = this.href || '#';
3316 cfg.name = this.anchor;
3317 if (this.html !== false || this.fa !== false) {
3320 if (this.href !== false) {
3321 cfg.href = this.href;
3325 if(this.alt !== false){
3330 if(this.target !== false) {
3331 cfg.target = this.target;
3337 initEvents: function() {
3339 if(!this.href || this.preventDefault){
3340 this.el.on('click', this.onClick, this);
3344 onClick : function(e)
3346 if(this.preventDefault){
3349 //Roo.log('img onclick');
3350 this.fireEvent('click', this, e);
3363 * @class Roo.bootstrap.Header
3364 * @extends Roo.bootstrap.Component
3365 * Bootstrap Header class
3366 * @cfg {String} html content of header
3367 * @cfg {Number} level (1|2|3|4|5|6) default 1
3370 * Create a new Header
3371 * @param {Object} config The config object
3375 Roo.bootstrap.Header = function(config){
3376 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3379 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3387 getAutoCreate : function(){
3392 tag: 'h' + (1 *this.level),
3393 html: this.html || ''
3405 * Ext JS Library 1.1.1
3406 * Copyright(c) 2006-2007, Ext JS, LLC.
3408 * Originally Released Under LGPL - original licence link has changed is not relivant.
3411 * <script type="text/javascript">
3415 * @class Roo.bootstrap.MenuMgr
3416 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3419 Roo.bootstrap.MenuMgr = function(){
3420 var menus, active, groups = {}, attached = false, lastShow = new Date();
3422 // private - called when first menu is created
3425 active = new Roo.util.MixedCollection();
3426 Roo.get(document).addKeyListener(27, function(){
3427 if(active.length > 0){
3435 if(active && active.length > 0){
3436 var c = active.clone();
3446 if(active.length < 1){
3447 Roo.get(document).un("mouseup", onMouseDown);
3455 var last = active.last();
3456 lastShow = new Date();
3459 Roo.get(document).on("mouseup", onMouseDown);
3464 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3465 m.parentMenu.activeChild = m;
3466 }else if(last && last.isVisible()){
3467 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3472 function onBeforeHide(m){
3474 m.activeChild.hide();
3476 if(m.autoHideTimer){
3477 clearTimeout(m.autoHideTimer);
3478 delete m.autoHideTimer;
3483 function onBeforeShow(m){
3484 var pm = m.parentMenu;
3485 if(!pm && !m.allowOtherMenus){
3487 }else if(pm && pm.activeChild && active != m){
3488 pm.activeChild.hide();
3492 // private this should really trigger on mouseup..
3493 function onMouseDown(e){
3494 Roo.log("on Mouse Up");
3496 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3497 Roo.log("MenuManager hideAll");
3506 function onBeforeCheck(mi, state){
3508 var g = groups[mi.group];
3509 for(var i = 0, l = g.length; i < l; i++){
3511 g[i].setChecked(false);
3520 * Hides all menus that are currently visible
3522 hideAll : function(){
3527 register : function(menu){
3531 menus[menu.id] = menu;
3532 menu.on("beforehide", onBeforeHide);
3533 menu.on("hide", onHide);
3534 menu.on("beforeshow", onBeforeShow);
3535 menu.on("show", onShow);
3537 if(g && menu.events["checkchange"]){
3541 groups[g].push(menu);
3542 menu.on("checkchange", onCheck);
3547 * Returns a {@link Roo.menu.Menu} object
3548 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3549 * be used to generate and return a new Menu instance.
3551 get : function(menu){
3552 if(typeof menu == "string"){ // menu id
3554 }else if(menu.events){ // menu instance
3557 /*else if(typeof menu.length == 'number'){ // array of menu items?
3558 return new Roo.bootstrap.Menu({items:menu});
3559 }else{ // otherwise, must be a config
3560 return new Roo.bootstrap.Menu(menu);
3567 unregister : function(menu){
3568 delete menus[menu.id];
3569 menu.un("beforehide", onBeforeHide);
3570 menu.un("hide", onHide);
3571 menu.un("beforeshow", onBeforeShow);
3572 menu.un("show", onShow);
3574 if(g && menu.events["checkchange"]){
3575 groups[g].remove(menu);
3576 menu.un("checkchange", onCheck);
3581 registerCheckable : function(menuItem){
3582 var g = menuItem.group;
3587 groups[g].push(menuItem);
3588 menuItem.on("beforecheckchange", onBeforeCheck);
3593 unregisterCheckable : function(menuItem){
3594 var g = menuItem.group;
3596 groups[g].remove(menuItem);
3597 menuItem.un("beforecheckchange", onBeforeCheck);
3609 * @class Roo.bootstrap.Menu
3610 * @extends Roo.bootstrap.Component
3611 * Bootstrap Menu class - container for MenuItems
3612 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3613 * @cfg {bool} hidden if the menu should be hidden when rendered.
3614 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3615 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3616 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3617 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3621 * @param {Object} config The config object
3625 Roo.bootstrap.Menu = function(config){
3627 if (config.type == 'treeview') {
3628 // normally menu's are drawn attached to the document to handle layering etc..
3629 // however treeview (used by the docs menu is drawn into the parent element)
3630 this.container_method = 'getChildContainer';
3633 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3634 if (this.registerMenu && this.type != 'treeview') {
3635 Roo.bootstrap.MenuMgr.register(this);
3642 * Fires before this menu is displayed (return false to block)
3643 * @param {Roo.menu.Menu} this
3648 * Fires before this menu is hidden (return false to block)
3649 * @param {Roo.menu.Menu} this
3654 * Fires after this menu is displayed
3655 * @param {Roo.menu.Menu} this
3660 * Fires after this menu is hidden
3661 * @param {Roo.menu.Menu} this
3666 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3667 * @param {Roo.menu.Menu} this
3668 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3669 * @param {Roo.EventObject} e
3674 * Fires when the mouse is hovering over this menu
3675 * @param {Roo.menu.Menu} this
3676 * @param {Roo.EventObject} e
3677 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3682 * Fires when the mouse exits this menu
3683 * @param {Roo.menu.Menu} this
3684 * @param {Roo.EventObject} e
3685 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3690 * Fires when a menu item contained in this menu is clicked
3691 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3692 * @param {Roo.EventObject} e
3696 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3699 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3703 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3706 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3708 registerMenu : true,
3710 menuItems :false, // stores the menu items..
3720 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3722 hideTrigger : false,
3727 getChildContainer : function() {
3731 getAutoCreate : function(){
3733 //if (['right'].indexOf(this.align)!==-1) {
3734 // cfg.cn[1].cls += ' pull-right'
3739 cls : 'dropdown-menu shadow' ,
3740 style : 'z-index:1000'
3744 if (this.type === 'submenu') {
3745 cfg.cls = 'submenu active';
3747 if (this.type === 'treeview') {
3748 cfg.cls = 'treeview-menu';
3753 initEvents : function() {
3755 // Roo.log("ADD event");
3756 // Roo.log(this.triggerEl.dom);
3757 if (this.triggerEl) {
3759 this.triggerEl.on('click', this.onTriggerClick, this);
3761 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3763 if (!this.hideTrigger) {
3764 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3765 // dropdown toggle on the 'a' in BS4?
3766 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3768 this.triggerEl.addClass('dropdown-toggle');
3774 this.el.on('touchstart' , this.onTouch, this);
3776 this.el.on('click' , this.onClick, this);
3778 this.el.on("mouseover", this.onMouseOver, this);
3779 this.el.on("mouseout", this.onMouseOut, this);
3783 findTargetItem : function(e)
3785 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3789 //Roo.log(t); Roo.log(t.id);
3791 //Roo.log(this.menuitems);
3792 return this.menuitems.get(t.id);
3794 //return this.items.get(t.menuItemId);
3800 onTouch : function(e)
3802 Roo.log("menu.onTouch");
3803 //e.stopEvent(); this make the user popdown broken
3807 onClick : function(e)
3809 Roo.log("menu.onClick");
3811 var t = this.findTargetItem(e);
3812 if(!t || t.isContainer){
3817 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3818 if(t == this.activeItem && t.shouldDeactivate(e)){
3819 this.activeItem.deactivate();
3820 delete this.activeItem;
3824 this.setActiveItem(t, true);
3832 Roo.log('pass click event');
3836 this.fireEvent("click", this, t, e);
3840 if(!t.href.length || t.href == '#'){
3841 (function() { _this.hide(); }).defer(100);
3846 onMouseOver : function(e){
3847 var t = this.findTargetItem(e);
3850 // if(t.canActivate && !t.disabled){
3851 // this.setActiveItem(t, true);
3855 this.fireEvent("mouseover", this, e, t);
3857 isVisible : function(){
3858 return !this.hidden;
3860 onMouseOut : function(e){
3861 var t = this.findTargetItem(e);
3864 // if(t == this.activeItem && t.shouldDeactivate(e)){
3865 // this.activeItem.deactivate();
3866 // delete this.activeItem;
3869 this.fireEvent("mouseout", this, e, t);
3874 * Displays this menu relative to another element
3875 * @param {String/HTMLElement/Roo.Element} element The element to align to
3876 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3877 * the element (defaults to this.defaultAlign)
3878 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3880 show : function(el, pos, parentMenu)
3882 if (false === this.fireEvent("beforeshow", this)) {
3883 Roo.log("show canceled");
3886 this.parentMenu = parentMenu;
3890 this.el.addClass('show'); // show otherwise we do not know how big we are..
3892 var xy = this.el.getAlignToXY(el, pos);
3894 // bl-tl << left align below
3895 // tl-bl << left align
3897 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3898 // if it goes to far to the right.. -> align left.
3899 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3902 // was left align - go right?
3903 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3906 // goes down the bottom
3907 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3909 var a = this.align.replace('?', '').split('-');
3910 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3914 this.showAt( xy , parentMenu, false);
3917 * Displays this menu at a specific xy position
3918 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3919 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3921 showAt : function(xy, parentMenu, /* private: */_e){
3922 this.parentMenu = parentMenu;
3927 this.fireEvent("beforeshow", this);
3928 //xy = this.el.adjustForConstraints(xy);
3932 this.hideMenuItems();
3933 this.hidden = false;
3934 if (this.triggerEl) {
3935 this.triggerEl.addClass('open');
3938 this.el.addClass('show');
3942 // reassign x when hitting right
3944 // reassign y when hitting bottom
3946 // but the list may align on trigger left or trigger top... should it be a properity?
3948 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3953 this.fireEvent("show", this);
3959 this.doFocus.defer(50, this);
3963 doFocus : function(){
3965 this.focusEl.focus();
3970 * Hides this menu and optionally all parent menus
3971 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3973 hide : function(deep)
3975 if (false === this.fireEvent("beforehide", this)) {
3976 Roo.log("hide canceled");
3979 this.hideMenuItems();
3980 if(this.el && this.isVisible()){
3982 if(this.activeItem){
3983 this.activeItem.deactivate();
3984 this.activeItem = null;
3986 if (this.triggerEl) {
3987 this.triggerEl.removeClass('open');
3990 this.el.removeClass('show');
3992 this.fireEvent("hide", this);
3994 if(deep === true && this.parentMenu){
3995 this.parentMenu.hide(true);
3999 onTriggerClick : function(e)
4001 Roo.log('trigger click');
4003 var target = e.getTarget();
4005 Roo.log(target.nodeName.toLowerCase());
4007 if(target.nodeName.toLowerCase() === 'i'){
4013 onTriggerPress : function(e)
4015 Roo.log('trigger press');
4016 //Roo.log(e.getTarget());
4017 // Roo.log(this.triggerEl.dom);
4019 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4020 var pel = Roo.get(e.getTarget());
4021 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4022 Roo.log('is treeview or dropdown?');
4026 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4030 if (this.isVisible()) {
4036 this.show(this.triggerEl, this.align, false);
4039 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4046 hideMenuItems : function()
4048 Roo.log("hide Menu Items");
4053 this.el.select('.open',true).each(function(aa) {
4055 aa.removeClass('open');
4059 addxtypeChild : function (tree, cntr) {
4060 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4062 this.menuitems.add(comp);
4074 this.getEl().dom.innerHTML = '';
4075 this.menuitems.clear();
4089 * @class Roo.bootstrap.MenuItem
4090 * @extends Roo.bootstrap.Component
4091 * Bootstrap MenuItem class
4092 * @cfg {String} html the menu label
4093 * @cfg {String} href the link
4094 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4095 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4096 * @cfg {Boolean} active used on sidebars to highlight active itesm
4097 * @cfg {String} fa favicon to show on left of menu item.
4098 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4102 * Create a new MenuItem
4103 * @param {Object} config The config object
4107 Roo.bootstrap.MenuItem = function(config){
4108 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4113 * The raw click event for the entire grid.
4114 * @param {Roo.bootstrap.MenuItem} this
4115 * @param {Roo.EventObject} e
4121 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4125 preventDefault: false,
4126 isContainer : false,
4130 getAutoCreate : function(){
4132 if(this.isContainer){
4135 cls: 'dropdown-menu-item '
4145 cls : 'dropdown-item',
4150 if (this.fa !== false) {
4153 cls : 'fa fa-' + this.fa
4162 cls: 'dropdown-menu-item',
4165 if (this.parent().type == 'treeview') {
4166 cfg.cls = 'treeview-menu';
4169 cfg.cls += ' active';
4174 anc.href = this.href || cfg.cn[0].href ;
4175 ctag.html = this.html || cfg.cn[0].html ;
4179 initEvents: function()
4181 if (this.parent().type == 'treeview') {
4182 this.el.select('a').on('click', this.onClick, this);
4186 this.menu.parentType = this.xtype;
4187 this.menu.triggerEl = this.el;
4188 this.menu = this.addxtype(Roo.apply({}, this.menu));
4192 onClick : function(e)
4194 Roo.log('item on click ');
4196 if(this.preventDefault){
4199 //this.parent().hideMenuItems();
4201 this.fireEvent('click', this, e);
4220 * @class Roo.bootstrap.MenuSeparator
4221 * @extends Roo.bootstrap.Component
4222 * Bootstrap MenuSeparator class
4225 * Create a new MenuItem
4226 * @param {Object} config The config object
4230 Roo.bootstrap.MenuSeparator = function(config){
4231 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4234 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4236 getAutoCreate : function(){
4255 * @class Roo.bootstrap.Modal
4256 * @extends Roo.bootstrap.Component
4258 * Bootstrap Modal class
4259 * @cfg {String} title Title of dialog
4260 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4261 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4262 * @cfg {Boolean} specificTitle default false
4263 * @cfg {Array} buttons Array of buttons or standard button set..
4264 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4265 * @cfg {Boolean} animate default true
4266 * @cfg {Boolean} allow_close default true
4267 * @cfg {Boolean} fitwindow default false
4268 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4269 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4270 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4271 * @cfg {String} size (sm|lg|xl) default empty
4272 * @cfg {Number} max_width set the max width of modal
4273 * @cfg {Boolean} editableTitle can the title be edited
4278 * Create a new Modal Dialog
4279 * @param {Object} config The config object
4282 Roo.bootstrap.Modal = function(config){
4283 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4288 * The raw btnclick event for the button
4289 * @param {Roo.EventObject} e
4294 * Fire when dialog resize
4295 * @param {Roo.bootstrap.Modal} this
4296 * @param {Roo.EventObject} e
4300 * @event titlechanged
4301 * Fire when the editable title has been changed
4302 * @param {Roo.bootstrap.Modal} this
4303 * @param {Roo.EventObject} value
4305 "titlechanged" : true
4308 this.buttons = this.buttons || [];
4311 this.tmpl = Roo.factory(this.tmpl);
4316 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4318 title : 'test dialog',
4328 specificTitle: false,
4330 buttonPosition: 'right',
4352 editableTitle : false,
4354 onRender : function(ct, position)
4356 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4359 var cfg = Roo.apply({}, this.getAutoCreate());
4362 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4364 //if (!cfg.name.length) {
4368 cfg.cls += ' ' + this.cls;
4371 cfg.style = this.style;
4373 this.el = Roo.get(document.body).createChild(cfg, position);
4375 //var type = this.el.dom.type;
4378 if(this.tabIndex !== undefined){
4379 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4382 this.dialogEl = this.el.select('.modal-dialog',true).first();
4383 this.bodyEl = this.el.select('.modal-body',true).first();
4384 this.closeEl = this.el.select('.modal-header .close', true).first();
4385 this.headerEl = this.el.select('.modal-header',true).first();
4386 this.titleEl = this.el.select('.modal-title',true).first();
4387 this.footerEl = this.el.select('.modal-footer',true).first();
4389 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4391 //this.el.addClass("x-dlg-modal");
4393 if (this.buttons.length) {
4394 Roo.each(this.buttons, function(bb) {
4395 var b = Roo.apply({}, bb);
4396 b.xns = b.xns || Roo.bootstrap;
4397 b.xtype = b.xtype || 'Button';
4398 if (typeof(b.listeners) == 'undefined') {
4399 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4402 var btn = Roo.factory(b);
4404 btn.render(this.getButtonContainer());
4408 // render the children.
4411 if(typeof(this.items) != 'undefined'){
4412 var items = this.items;
4415 for(var i =0;i < items.length;i++) {
4416 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4420 this.items = nitems;
4422 // where are these used - they used to be body/close/footer
4426 //this.el.addClass([this.fieldClass, this.cls]);
4430 getAutoCreate : function()
4432 // we will default to modal-body-overflow - might need to remove or make optional later.
4434 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4435 html : this.html || ''
4440 cls : 'modal-title',
4444 if(this.specificTitle){ // WTF is this?
4449 if (this.allow_close && Roo.bootstrap.version == 3) {
4459 if (this.editableTitle) {
4461 cls: 'form-control roo-editable-title d-none',
4467 if (this.allow_close && Roo.bootstrap.version == 4) {
4477 if(this.size.length){
4478 size = 'modal-' + this.size;
4481 var footer = Roo.bootstrap.version == 3 ?
4483 cls : 'modal-footer',
4487 cls: 'btn-' + this.buttonPosition
4492 { // BS4 uses mr-auto on left buttons....
4493 cls : 'modal-footer'
4504 cls: "modal-dialog " + size,
4507 cls : "modal-content",
4510 cls : 'modal-header',
4525 modal.cls += ' fade';
4531 getChildContainer : function() {
4536 getButtonContainer : function() {
4538 return Roo.bootstrap.version == 4 ?
4539 this.el.select('.modal-footer',true).first()
4540 : this.el.select('.modal-footer div',true).first();
4543 initEvents : function()
4545 if (this.allow_close) {
4546 this.closeEl.on('click', this.hide, this);
4548 Roo.EventManager.onWindowResize(this.resize, this, true);
4549 if (this.editableTitle) {
4550 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4551 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4552 this.headerEditEl.on('keyup', function(e) {
4553 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4554 this.toggleHeaderInput(false)
4557 this.headerEditEl.on('blur', function(e) {
4558 this.toggleHeaderInput(false)
4567 this.maskEl.setSize(
4568 Roo.lib.Dom.getViewWidth(true),
4569 Roo.lib.Dom.getViewHeight(true)
4572 if (this.fitwindow) {
4574 this.dialogEl.setStyle( { 'max-width' : '100%' });
4576 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4577 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4582 if(this.max_width !== 0) {
4584 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4587 this.setSize(w, this.height);
4591 if(this.max_height) {
4592 this.setSize(w,Math.min(
4594 Roo.lib.Dom.getViewportHeight(true) - 60
4600 if(!this.fit_content) {
4601 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4605 this.setSize(w, Math.min(
4607 this.headerEl.getHeight() +
4608 this.footerEl.getHeight() +
4609 this.getChildHeight(this.bodyEl.dom.childNodes),
4610 Roo.lib.Dom.getViewportHeight(true) - 60)
4616 setSize : function(w,h)
4627 if (!this.rendered) {
4630 this.toggleHeaderInput(false);
4631 //this.el.setStyle('display', 'block');
4632 this.el.removeClass('hideing');
4633 this.el.dom.style.display='block';
4635 Roo.get(document.body).addClass('modal-open');
4637 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4640 this.el.addClass('show');
4641 this.el.addClass('in');
4644 this.el.addClass('show');
4645 this.el.addClass('in');
4648 // not sure how we can show data in here..
4650 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4653 Roo.get(document.body).addClass("x-body-masked");
4655 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4656 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4657 this.maskEl.dom.style.display = 'block';
4658 this.maskEl.addClass('show');
4663 this.fireEvent('show', this);
4665 // set zindex here - otherwise it appears to be ignored...
4666 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4669 this.items.forEach( function(e) {
4670 e.layout ? e.layout() : false;
4678 if(this.fireEvent("beforehide", this) !== false){
4680 this.maskEl.removeClass('show');
4682 this.maskEl.dom.style.display = '';
4683 Roo.get(document.body).removeClass("x-body-masked");
4684 this.el.removeClass('in');
4685 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4687 if(this.animate){ // why
4688 this.el.addClass('hideing');
4689 this.el.removeClass('show');
4691 if (!this.el.hasClass('hideing')) {
4692 return; // it's been shown again...
4695 this.el.dom.style.display='';
4697 Roo.get(document.body).removeClass('modal-open');
4698 this.el.removeClass('hideing');
4702 this.el.removeClass('show');
4703 this.el.dom.style.display='';
4704 Roo.get(document.body).removeClass('modal-open');
4707 this.fireEvent('hide', this);
4710 isVisible : function()
4713 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4717 addButton : function(str, cb)
4721 var b = Roo.apply({}, { html : str } );
4722 b.xns = b.xns || Roo.bootstrap;
4723 b.xtype = b.xtype || 'Button';
4724 if (typeof(b.listeners) == 'undefined') {
4725 b.listeners = { click : cb.createDelegate(this) };
4728 var btn = Roo.factory(b);
4730 btn.render(this.getButtonContainer());
4736 setDefaultButton : function(btn)
4738 //this.el.select('.modal-footer').()
4741 resizeTo: function(w,h)
4743 this.dialogEl.setWidth(w);
4745 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4747 this.bodyEl.setHeight(h - diff);
4749 this.fireEvent('resize', this);
4752 setContentSize : function(w, h)
4756 onButtonClick: function(btn,e)
4759 this.fireEvent('btnclick', btn.name, e);
4762 * Set the title of the Dialog
4763 * @param {String} str new Title
4765 setTitle: function(str) {
4766 this.titleEl.dom.innerHTML = str;
4770 * Set the body of the Dialog
4771 * @param {String} str new Title
4773 setBody: function(str) {
4774 this.bodyEl.dom.innerHTML = str;
4777 * Set the body of the Dialog using the template
4778 * @param {Obj} data - apply this data to the template and replace the body contents.
4780 applyBody: function(obj)
4783 Roo.log("Error - using apply Body without a template");
4786 this.tmpl.overwrite(this.bodyEl, obj);
4789 getChildHeight : function(child_nodes)
4793 child_nodes.length == 0
4798 var child_height = 0;
4800 for(var i = 0; i < child_nodes.length; i++) {
4803 * for modal with tabs...
4804 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4806 var layout_childs = child_nodes[i].childNodes;
4808 for(var j = 0; j < layout_childs.length; j++) {
4810 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4812 var layout_body_childs = layout_childs[j].childNodes;
4814 for(var k = 0; k < layout_body_childs.length; k++) {
4816 if(layout_body_childs[k].classList.contains('navbar')) {
4817 child_height += layout_body_childs[k].offsetHeight;
4821 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4823 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4825 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4827 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4828 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4843 child_height += child_nodes[i].offsetHeight;
4844 // Roo.log(child_nodes[i].offsetHeight);
4847 return child_height;
4849 toggleHeaderInput : function(is_edit)
4851 if (!this.editableTitle) {
4852 return; // not editable.
4854 if (is_edit && this.is_header_editing) {
4855 return; // already editing..
4859 this.headerEditEl.dom.value = this.title;
4860 this.headerEditEl.removeClass('d-none');
4861 this.headerEditEl.dom.focus();
4862 this.titleEl.addClass('d-none');
4864 this.is_header_editing = true;
4867 // flip back to not editing.
4868 this.title = this.headerEditEl.dom.value;
4869 this.headerEditEl.addClass('d-none');
4870 this.titleEl.removeClass('d-none');
4871 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4872 this.is_header_editing = false;
4873 this.fireEvent('titlechanged', this, this.title);
4882 Roo.apply(Roo.bootstrap.Modal, {
4884 * Button config that displays a single OK button
4893 * Button config that displays Yes and No buttons
4909 * Button config that displays OK and Cancel buttons
4924 * Button config that displays Yes, No and Cancel buttons
4949 * messagebox - can be used as a replace
4953 * @class Roo.MessageBox
4954 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4958 Roo.Msg.alert('Status', 'Changes saved successfully.');
4960 // Prompt for user data:
4961 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4963 // process text value...
4967 // Show a dialog using config options:
4969 title:'Save Changes?',
4970 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4971 buttons: Roo.Msg.YESNOCANCEL,
4978 Roo.bootstrap.MessageBox = function(){
4979 var dlg, opt, mask, waitTimer;
4980 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4981 var buttons, activeTextEl, bwidth;
4985 var handleButton = function(button){
4987 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4991 var handleHide = function(){
4993 dlg.el.removeClass(opt.cls);
4996 // Roo.TaskMgr.stop(waitTimer);
4997 // waitTimer = null;
5002 var updateButtons = function(b){
5005 buttons["ok"].hide();
5006 buttons["cancel"].hide();
5007 buttons["yes"].hide();
5008 buttons["no"].hide();
5009 dlg.footerEl.hide();
5013 dlg.footerEl.show();
5014 for(var k in buttons){
5015 if(typeof buttons[k] != "function"){
5018 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5019 width += buttons[k].el.getWidth()+15;
5029 var handleEsc = function(d, k, e){
5030 if(opt && opt.closable !== false){
5040 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5041 * @return {Roo.BasicDialog} The BasicDialog element
5043 getDialog : function(){
5045 dlg = new Roo.bootstrap.Modal( {
5048 //constraintoviewport:false,
5050 //collapsible : false,
5055 //buttonAlign:"center",
5056 closeClick : function(){
5057 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5060 handleButton("cancel");
5065 dlg.on("hide", handleHide);
5067 //dlg.addKeyListener(27, handleEsc);
5069 this.buttons = buttons;
5070 var bt = this.buttonText;
5071 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5072 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5073 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5074 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5076 bodyEl = dlg.bodyEl.createChild({
5078 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5079 '<textarea class="roo-mb-textarea"></textarea>' +
5080 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5082 msgEl = bodyEl.dom.firstChild;
5083 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5084 textboxEl.enableDisplayMode();
5085 textboxEl.addKeyListener([10,13], function(){
5086 if(dlg.isVisible() && opt && opt.buttons){
5089 }else if(opt.buttons.yes){
5090 handleButton("yes");
5094 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5095 textareaEl.enableDisplayMode();
5096 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5097 progressEl.enableDisplayMode();
5099 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5100 var pf = progressEl.dom.firstChild;
5102 pp = Roo.get(pf.firstChild);
5103 pp.setHeight(pf.offsetHeight);
5111 * Updates the message box body text
5112 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5113 * the XHTML-compliant non-breaking space character '&#160;')
5114 * @return {Roo.MessageBox} This message box
5116 updateText : function(text)
5118 if(!dlg.isVisible() && !opt.width){
5119 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5120 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5122 msgEl.innerHTML = text || ' ';
5124 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5125 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5127 Math.min(opt.width || cw , this.maxWidth),
5128 Math.max(opt.minWidth || this.minWidth, bwidth)
5131 activeTextEl.setWidth(w);
5133 if(dlg.isVisible()){
5134 dlg.fixedcenter = false;
5136 // to big, make it scroll. = But as usual stupid IE does not support
5139 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5140 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5141 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5143 bodyEl.dom.style.height = '';
5144 bodyEl.dom.style.overflowY = '';
5147 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5149 bodyEl.dom.style.overflowX = '';
5152 dlg.setContentSize(w, bodyEl.getHeight());
5153 if(dlg.isVisible()){
5154 dlg.fixedcenter = true;
5160 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5161 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5162 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5163 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5164 * @return {Roo.MessageBox} This message box
5166 updateProgress : function(value, text){
5168 this.updateText(text);
5171 if (pp) { // weird bug on my firefox - for some reason this is not defined
5172 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5173 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5179 * Returns true if the message box is currently displayed
5180 * @return {Boolean} True if the message box is visible, else false
5182 isVisible : function(){
5183 return dlg && dlg.isVisible();
5187 * Hides the message box if it is displayed
5190 if(this.isVisible()){
5196 * Displays a new message box, or reinitializes an existing message box, based on the config options
5197 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5198 * The following config object properties are supported:
5200 Property Type Description
5201 ---------- --------------- ------------------------------------------------------------------------------------
5202 animEl String/Element An id or Element from which the message box should animate as it opens and
5203 closes (defaults to undefined)
5204 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5205 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5206 closable Boolean False to hide the top-right close button (defaults to true). Note that
5207 progress and wait dialogs will ignore this property and always hide the
5208 close button as they can only be closed programmatically.
5209 cls String A custom CSS class to apply to the message box element
5210 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5211 displayed (defaults to 75)
5212 fn Function A callback function to execute after closing the dialog. The arguments to the
5213 function will be btn (the name of the button that was clicked, if applicable,
5214 e.g. "ok"), and text (the value of the active text field, if applicable).
5215 Progress and wait dialogs will ignore this option since they do not respond to
5216 user actions and can only be closed programmatically, so any required function
5217 should be called by the same code after it closes the dialog.
5218 icon String A CSS class that provides a background image to be used as an icon for
5219 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5220 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5221 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5222 modal Boolean False to allow user interaction with the page while the message box is
5223 displayed (defaults to true)
5224 msg String A string that will replace the existing message box body text (defaults
5225 to the XHTML-compliant non-breaking space character ' ')
5226 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5227 progress Boolean True to display a progress bar (defaults to false)
5228 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5229 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5230 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5231 title String The title text
5232 value String The string value to set into the active textbox element if displayed
5233 wait Boolean True to display a progress bar (defaults to false)
5234 width Number The width of the dialog in pixels
5241 msg: 'Please enter your address:',
5243 buttons: Roo.MessageBox.OKCANCEL,
5246 animEl: 'addAddressBtn'
5249 * @param {Object} config Configuration options
5250 * @return {Roo.MessageBox} This message box
5252 show : function(options)
5255 // this causes nightmares if you show one dialog after another
5256 // especially on callbacks..
5258 if(this.isVisible()){
5261 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5262 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5263 Roo.log("New Dialog Message:" + options.msg )
5264 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5265 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5268 var d = this.getDialog();
5270 d.setTitle(opt.title || " ");
5271 d.closeEl.setDisplayed(opt.closable !== false);
5272 activeTextEl = textboxEl;
5273 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5278 textareaEl.setHeight(typeof opt.multiline == "number" ?
5279 opt.multiline : this.defaultTextHeight);
5280 activeTextEl = textareaEl;
5289 progressEl.setDisplayed(opt.progress === true);
5291 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5293 this.updateProgress(0);
5294 activeTextEl.dom.value = opt.value || "";
5296 dlg.setDefaultButton(activeTextEl);
5298 var bs = opt.buttons;
5302 }else if(bs && bs.yes){
5303 db = buttons["yes"];
5305 dlg.setDefaultButton(db);
5307 bwidth = updateButtons(opt.buttons);
5308 this.updateText(opt.msg);
5310 d.el.addClass(opt.cls);
5312 d.proxyDrag = opt.proxyDrag === true;
5313 d.modal = opt.modal !== false;
5314 d.mask = opt.modal !== false ? mask : false;
5316 // force it to the end of the z-index stack so it gets a cursor in FF
5317 document.body.appendChild(dlg.el.dom);
5318 d.animateTarget = null;
5319 d.show(options.animEl);
5325 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5326 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5327 * and closing the message box when the process is complete.
5328 * @param {String} title The title bar text
5329 * @param {String} msg The message box body text
5330 * @return {Roo.MessageBox} This message box
5332 progress : function(title, msg){
5339 minWidth: this.minProgressWidth,
5346 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5347 * If a callback function is passed it will be called after the user clicks the button, and the
5348 * id of the button that was clicked will be passed as the only parameter to the callback
5349 * (could also be the top-right close button).
5350 * @param {String} title The title bar text
5351 * @param {String} msg The message box body text
5352 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5353 * @param {Object} scope (optional) The scope of the callback function
5354 * @return {Roo.MessageBox} This message box
5356 alert : function(title, msg, fn, scope)
5371 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5372 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5373 * You are responsible for closing the message box when the process is complete.
5374 * @param {String} msg The message box body text
5375 * @param {String} title (optional) The title bar text
5376 * @return {Roo.MessageBox} This message box
5378 wait : function(msg, title){
5389 waitTimer = Roo.TaskMgr.start({
5391 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5399 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5400 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5401 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5402 * @param {String} title The title bar text
5403 * @param {String} msg The message box body text
5404 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5405 * @param {Object} scope (optional) The scope of the callback function
5406 * @return {Roo.MessageBox} This message box
5408 confirm : function(title, msg, fn, scope){
5412 buttons: this.YESNO,
5421 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5422 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5423 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5424 * (could also be the top-right close button) and the text that was entered will be passed as the two
5425 * parameters to the callback.
5426 * @param {String} title The title bar text
5427 * @param {String} msg The message box body text
5428 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5429 * @param {Object} scope (optional) The scope of the callback function
5430 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5431 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5432 * @return {Roo.MessageBox} This message box
5434 prompt : function(title, msg, fn, scope, multiline){
5438 buttons: this.OKCANCEL,
5443 multiline: multiline,
5450 * Button config that displays a single OK button
5455 * Button config that displays Yes and No buttons
5458 YESNO : {yes:true, no:true},
5460 * Button config that displays OK and Cancel buttons
5463 OKCANCEL : {ok:true, cancel:true},
5465 * Button config that displays Yes, No and Cancel buttons
5468 YESNOCANCEL : {yes:true, no:true, cancel:true},
5471 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5474 defaultTextHeight : 75,
5476 * The maximum width in pixels of the message box (defaults to 600)
5481 * The minimum width in pixels of the message box (defaults to 100)
5486 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5487 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5490 minProgressWidth : 250,
5492 * An object containing the default button text strings that can be overriden for localized language support.
5493 * Supported properties are: ok, cancel, yes and no.
5494 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5507 * Shorthand for {@link Roo.MessageBox}
5509 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5510 Roo.Msg = Roo.Msg || Roo.MessageBox;
5519 * @class Roo.bootstrap.Navbar
5520 * @extends Roo.bootstrap.Component
5521 * Bootstrap Navbar class
5524 * Create a new Navbar
5525 * @param {Object} config The config object
5529 Roo.bootstrap.Navbar = function(config){
5530 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5534 * @event beforetoggle
5535 * Fire before toggle the menu
5536 * @param {Roo.EventObject} e
5538 "beforetoggle" : true
5542 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5551 getAutoCreate : function(){
5554 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5558 initEvents :function ()
5560 //Roo.log(this.el.select('.navbar-toggle',true));
5561 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5568 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5570 var size = this.el.getSize();
5571 this.maskEl.setSize(size.width, size.height);
5572 this.maskEl.enableDisplayMode("block");
5581 getChildContainer : function()
5583 if (this.el && this.el.select('.collapse').getCount()) {
5584 return this.el.select('.collapse',true).first();
5599 onToggle : function()
5602 if(this.fireEvent('beforetoggle', this) === false){
5605 var ce = this.el.select('.navbar-collapse',true).first();
5607 if (!ce.hasClass('show')) {
5617 * Expand the navbar pulldown
5619 expand : function ()
5622 var ce = this.el.select('.navbar-collapse',true).first();
5623 if (ce.hasClass('collapsing')) {
5626 ce.dom.style.height = '';
5628 ce.addClass('in'); // old...
5629 ce.removeClass('collapse');
5630 ce.addClass('show');
5631 var h = ce.getHeight();
5633 ce.removeClass('show');
5634 // at this point we should be able to see it..
5635 ce.addClass('collapsing');
5637 ce.setHeight(0); // resize it ...
5638 ce.on('transitionend', function() {
5639 //Roo.log('done transition');
5640 ce.removeClass('collapsing');
5641 ce.addClass('show');
5642 ce.removeClass('collapse');
5644 ce.dom.style.height = '';
5645 }, this, { single: true} );
5647 ce.dom.scrollTop = 0;
5650 * Collapse the navbar pulldown
5652 collapse : function()
5654 var ce = this.el.select('.navbar-collapse',true).first();
5656 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5657 // it's collapsed or collapsing..
5660 ce.removeClass('in'); // old...
5661 ce.setHeight(ce.getHeight());
5662 ce.removeClass('show');
5663 ce.addClass('collapsing');
5665 ce.on('transitionend', function() {
5666 ce.dom.style.height = '';
5667 ce.removeClass('collapsing');
5668 ce.addClass('collapse');
5669 }, this, { single: true} );
5689 * @class Roo.bootstrap.NavSimplebar
5690 * @extends Roo.bootstrap.Navbar
5691 * Bootstrap Sidebar class
5693 * @cfg {Boolean} inverse is inverted color
5695 * @cfg {String} type (nav | pills | tabs)
5696 * @cfg {Boolean} arrangement stacked | justified
5697 * @cfg {String} align (left | right) alignment
5699 * @cfg {Boolean} main (true|false) main nav bar? default false
5700 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5702 * @cfg {String} tag (header|footer|nav|div) default is nav
5704 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5708 * Create a new Sidebar
5709 * @param {Object} config The config object
5713 Roo.bootstrap.NavSimplebar = function(config){
5714 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5717 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5733 getAutoCreate : function(){
5737 tag : this.tag || 'div',
5738 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5740 if (['light','white'].indexOf(this.weight) > -1) {
5741 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5743 cfg.cls += ' bg-' + this.weight;
5746 cfg.cls += ' navbar-inverse';
5750 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5752 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5761 cls: 'nav nav-' + this.xtype,
5767 this.type = this.type || 'nav';
5768 if (['tabs','pills'].indexOf(this.type) != -1) {
5769 cfg.cn[0].cls += ' nav-' + this.type
5773 if (this.type!=='nav') {
5774 Roo.log('nav type must be nav/tabs/pills')
5776 cfg.cn[0].cls += ' navbar-nav'
5782 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5783 cfg.cn[0].cls += ' nav-' + this.arrangement;
5787 if (this.align === 'right') {
5788 cfg.cn[0].cls += ' navbar-right';
5813 * navbar-expand-md fixed-top
5817 * @class Roo.bootstrap.NavHeaderbar
5818 * @extends Roo.bootstrap.NavSimplebar
5819 * Bootstrap Sidebar class
5821 * @cfg {String} brand what is brand
5822 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5823 * @cfg {String} brand_href href of the brand
5824 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5825 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5826 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5827 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5830 * Create a new Sidebar
5831 * @param {Object} config The config object
5835 Roo.bootstrap.NavHeaderbar = function(config){
5836 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5840 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5847 desktopCenter : false,
5850 getAutoCreate : function(){
5853 tag: this.nav || 'nav',
5854 cls: 'navbar navbar-expand-md',
5860 if (this.desktopCenter) {
5861 cn.push({cls : 'container', cn : []});
5869 cls: 'navbar-toggle navbar-toggler',
5870 'data-toggle': 'collapse',
5875 html: 'Toggle navigation'
5879 cls: 'icon-bar navbar-toggler-icon'
5892 cn.push( Roo.bootstrap.version == 4 ? btn : {
5894 cls: 'navbar-header',
5903 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5907 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5909 if (['light','white'].indexOf(this.weight) > -1) {
5910 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5912 cfg.cls += ' bg-' + this.weight;
5915 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5916 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5918 // tag can override this..
5920 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5923 if (this.brand !== '') {
5924 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5925 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5927 href: this.brand_href ? this.brand_href : '#',
5928 cls: 'navbar-brand',
5936 cfg.cls += ' main-nav';
5944 getHeaderChildContainer : function()
5946 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5947 return this.el.select('.navbar-header',true).first();
5950 return this.getChildContainer();
5953 getChildContainer : function()
5956 return this.el.select('.roo-navbar-collapse',true).first();
5961 initEvents : function()
5963 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5965 if (this.autohide) {
5970 Roo.get(document).on('scroll',function(e) {
5971 var ns = Roo.get(document).getScroll().top;
5972 var os = prevScroll;
5976 ft.removeClass('slideDown');
5977 ft.addClass('slideUp');
5980 ft.removeClass('slideUp');
5981 ft.addClass('slideDown');
6002 * @class Roo.bootstrap.NavSidebar
6003 * @extends Roo.bootstrap.Navbar
6004 * Bootstrap Sidebar class
6007 * Create a new Sidebar
6008 * @param {Object} config The config object
6012 Roo.bootstrap.NavSidebar = function(config){
6013 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6016 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6018 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6020 getAutoCreate : function(){
6025 cls: 'sidebar sidebar-nav'
6047 * @class Roo.bootstrap.NavGroup
6048 * @extends Roo.bootstrap.Component
6049 * Bootstrap NavGroup class
6050 * @cfg {String} align (left|right)
6051 * @cfg {Boolean} inverse
6052 * @cfg {String} type (nav|pills|tab) default nav
6053 * @cfg {String} navId - reference Id for navbar.
6054 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6057 * Create a new nav group
6058 * @param {Object} config The config object
6061 Roo.bootstrap.NavGroup = function(config){
6062 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6065 Roo.bootstrap.NavGroup.register(this);
6069 * Fires when the active item changes
6070 * @param {Roo.bootstrap.NavGroup} this
6071 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6072 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6079 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6091 getAutoCreate : function()
6093 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6099 if (Roo.bootstrap.version == 4) {
6100 if (['tabs','pills'].indexOf(this.type) != -1) {
6101 cfg.cls += ' nav-' + this.type;
6103 // trying to remove so header bar can right align top?
6104 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6105 // do not use on header bar...
6106 cfg.cls += ' navbar-nav';
6111 if (['tabs','pills'].indexOf(this.type) != -1) {
6112 cfg.cls += ' nav-' + this.type
6114 if (this.type !== 'nav') {
6115 Roo.log('nav type must be nav/tabs/pills')
6117 cfg.cls += ' navbar-nav'
6121 if (this.parent() && this.parent().sidebar) {
6124 cls: 'dashboard-menu sidebar-menu'
6130 if (this.form === true) {
6133 cls: 'navbar-form form-inline'
6135 //nav navbar-right ml-md-auto
6136 if (this.align === 'right') {
6137 cfg.cls += ' navbar-right ml-md-auto';
6139 cfg.cls += ' navbar-left';
6143 if (this.align === 'right') {
6144 cfg.cls += ' navbar-right ml-md-auto';
6146 cfg.cls += ' mr-auto';
6150 cfg.cls += ' navbar-inverse';
6158 * sets the active Navigation item
6159 * @param {Roo.bootstrap.NavItem} the new current navitem
6161 setActiveItem : function(item)
6164 Roo.each(this.navItems, function(v){
6169 v.setActive(false, true);
6176 item.setActive(true, true);
6177 this.fireEvent('changed', this, item, prev);
6182 * gets the active Navigation item
6183 * @return {Roo.bootstrap.NavItem} the current navitem
6185 getActive : function()
6189 Roo.each(this.navItems, function(v){
6200 indexOfNav : function()
6204 Roo.each(this.navItems, function(v,i){
6215 * adds a Navigation item
6216 * @param {Roo.bootstrap.NavItem} the navitem to add
6218 addItem : function(cfg)
6220 if (this.form && Roo.bootstrap.version == 4) {
6223 var cn = new Roo.bootstrap.NavItem(cfg);
6225 cn.parentId = this.id;
6226 cn.onRender(this.el, null);
6230 * register a Navigation item
6231 * @param {Roo.bootstrap.NavItem} the navitem to add
6233 register : function(item)
6235 this.navItems.push( item);
6236 item.navId = this.navId;
6241 * clear all the Navigation item
6244 clearAll : function()
6247 this.el.dom.innerHTML = '';
6250 getNavItem: function(tabId)
6253 Roo.each(this.navItems, function(e) {
6254 if (e.tabId == tabId) {
6264 setActiveNext : function()
6266 var i = this.indexOfNav(this.getActive());
6267 if (i > this.navItems.length) {
6270 this.setActiveItem(this.navItems[i+1]);
6272 setActivePrev : function()
6274 var i = this.indexOfNav(this.getActive());
6278 this.setActiveItem(this.navItems[i-1]);
6280 clearWasActive : function(except) {
6281 Roo.each(this.navItems, function(e) {
6282 if (e.tabId != except.tabId && e.was_active) {
6283 e.was_active = false;
6290 getWasActive : function ()
6293 Roo.each(this.navItems, function(e) {
6308 Roo.apply(Roo.bootstrap.NavGroup, {
6312 * register a Navigation Group
6313 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6315 register : function(navgrp)
6317 this.groups[navgrp.navId] = navgrp;
6321 * fetch a Navigation Group based on the navigation ID
6322 * @param {string} the navgroup to add
6323 * @returns {Roo.bootstrap.NavGroup} the navgroup
6325 get: function(navId) {
6326 if (typeof(this.groups[navId]) == 'undefined') {
6328 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6330 return this.groups[navId] ;
6345 * @class Roo.bootstrap.NavItem
6346 * @extends Roo.bootstrap.Component
6347 * Bootstrap Navbar.NavItem class
6348 * @cfg {String} href link to
6349 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6350 * @cfg {Boolean} button_outline show and outlined button
6351 * @cfg {String} html content of button
6352 * @cfg {String} badge text inside badge
6353 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6354 * @cfg {String} glyphicon DEPRICATED - use fa
6355 * @cfg {String} icon DEPRICATED - use fa
6356 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6357 * @cfg {Boolean} active Is item active
6358 * @cfg {Boolean} disabled Is item disabled
6359 * @cfg {String} linkcls Link Class
6360 * @cfg {Boolean} preventDefault (true | false) default false
6361 * @cfg {String} tabId the tab that this item activates.
6362 * @cfg {String} tagtype (a|span) render as a href or span?
6363 * @cfg {Boolean} animateRef (true|false) link to element default false
6366 * Create a new Navbar Item
6367 * @param {Object} config The config object
6369 Roo.bootstrap.NavItem = function(config){
6370 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6375 * The raw click event for the entire grid.
6376 * @param {Roo.EventObject} e
6381 * Fires when the active item active state changes
6382 * @param {Roo.bootstrap.NavItem} this
6383 * @param {boolean} state the new state
6389 * Fires when scroll to element
6390 * @param {Roo.bootstrap.NavItem} this
6391 * @param {Object} options
6392 * @param {Roo.EventObject} e
6400 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6409 preventDefault : false,
6417 button_outline : false,
6421 getAutoCreate : function(){
6428 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6431 cfg.cls += ' active' ;
6433 if (this.disabled) {
6434 cfg.cls += ' disabled';
6438 if (this.button_weight.length) {
6439 cfg.tag = this.href ? 'a' : 'button';
6440 cfg.html = this.html || '';
6441 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6443 cfg.href = this.href;
6446 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6448 cfg.cls += " nav-html";
6451 // menu .. should add dropdown-menu class - so no need for carat..
6453 if (this.badge !== '') {
6455 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6460 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6464 href : this.href || "#",
6465 html: this.html || '',
6469 if (this.tagtype == 'a') {
6470 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6474 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475 } else if (this.fa) {
6476 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6477 } else if(this.glyphicon) {
6478 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6480 cfg.cn[0].cls += " nav-html";
6484 cfg.cn[0].html += " <span class='caret'></span>";
6488 if (this.badge !== '') {
6489 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6497 onRender : function(ct, position)
6499 // Roo.log("Call onRender: " + this.xtype);
6500 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6504 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6505 this.navLink = this.el.select('.nav-link',true).first();
6506 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6511 initEvents: function()
6513 if (typeof (this.menu) != 'undefined') {
6514 this.menu.parentType = this.xtype;
6515 this.menu.triggerEl = this.el;
6516 this.menu = this.addxtype(Roo.apply({}, this.menu));
6519 this.el.on('click', this.onClick, this);
6521 //if(this.tagtype == 'span'){
6522 // this.el.select('span',true).on('click', this.onClick, this);
6525 // at this point parent should be available..
6526 this.parent().register(this);
6529 onClick : function(e)
6531 if (e.getTarget('.dropdown-menu-item')) {
6532 // did you click on a menu itemm.... - then don't trigger onclick..
6537 this.preventDefault ||
6540 Roo.log("NavItem - prevent Default?");
6544 if (this.disabled) {
6548 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6549 if (tg && tg.transition) {
6550 Roo.log("waiting for the transitionend");
6556 //Roo.log("fire event clicked");
6557 if(this.fireEvent('click', this, e) === false){
6561 if(this.tagtype == 'span'){
6565 //Roo.log(this.href);
6566 var ael = this.el.select('a',true).first();
6569 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6570 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6571 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6572 return; // ignore... - it's a 'hash' to another page.
6574 Roo.log("NavItem - prevent Default?");
6576 this.scrollToElement(e);
6580 var p = this.parent();
6582 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6583 if (typeof(p.setActiveItem) !== 'undefined') {
6584 p.setActiveItem(this);
6588 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6589 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6590 // remove the collapsed menu expand...
6591 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6595 isActive: function () {
6598 setActive : function(state, fire, is_was_active)
6600 if (this.active && !state && this.navId) {
6601 this.was_active = true;
6602 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6604 nv.clearWasActive(this);
6608 this.active = state;
6611 this.el.removeClass('active');
6612 this.navLink ? this.navLink.removeClass('active') : false;
6613 } else if (!this.el.hasClass('active')) {
6615 this.el.addClass('active');
6616 if (Roo.bootstrap.version == 4 && this.navLink ) {
6617 this.navLink.addClass('active');
6622 this.fireEvent('changed', this, state);
6625 // show a panel if it's registered and related..
6627 if (!this.navId || !this.tabId || !state || is_was_active) {
6631 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6635 var pan = tg.getPanelByName(this.tabId);
6639 // if we can not flip to new panel - go back to old nav highlight..
6640 if (false == tg.showPanel(pan)) {
6641 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6643 var onav = nv.getWasActive();
6645 onav.setActive(true, false, true);
6654 // this should not be here...
6655 setDisabled : function(state)
6657 this.disabled = state;
6659 this.el.removeClass('disabled');
6660 } else if (!this.el.hasClass('disabled')) {
6661 this.el.addClass('disabled');
6667 * Fetch the element to display the tooltip on.
6668 * @return {Roo.Element} defaults to this.el
6670 tooltipEl : function()
6672 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6675 scrollToElement : function(e)
6677 var c = document.body;
6680 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6682 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6683 c = document.documentElement;
6686 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6692 var o = target.calcOffsetsTo(c);
6699 this.fireEvent('scrollto', this, options, e);
6701 Roo.get(c).scrollTo('top', options.value, true);
6706 * Set the HTML (text content) of the item
6707 * @param {string} html content for the nav item
6709 setHtml : function(html)
6712 this.htmlEl.dom.innerHTML = html;
6724 * <span> icon </span>
6725 * <span> text </span>
6726 * <span>badge </span>
6730 * @class Roo.bootstrap.NavSidebarItem
6731 * @extends Roo.bootstrap.NavItem
6732 * Bootstrap Navbar.NavSidebarItem class
6733 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6734 * {Boolean} open is the menu open
6735 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6736 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6737 * {String} buttonSize (sm|md|lg)the extra classes for the button
6738 * {Boolean} showArrow show arrow next to the text (default true)
6740 * Create a new Navbar Button
6741 * @param {Object} config The config object
6743 Roo.bootstrap.NavSidebarItem = function(config){
6744 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6749 * The raw click event for the entire grid.
6750 * @param {Roo.EventObject} e
6755 * Fires when the active item active state changes
6756 * @param {Roo.bootstrap.NavSidebarItem} this
6757 * @param {boolean} state the new state
6765 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6767 badgeWeight : 'default',
6773 buttonWeight : 'default',
6779 getAutoCreate : function(){
6784 href : this.href || '#',
6790 if(this.buttonView){
6793 href : this.href || '#',
6794 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6807 cfg.cls += ' active';
6810 if (this.disabled) {
6811 cfg.cls += ' disabled';
6814 cfg.cls += ' open x-open';
6817 if (this.glyphicon || this.icon) {
6818 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6819 a.cn.push({ tag : 'i', cls : c }) ;
6822 if(!this.buttonView){
6825 html : this.html || ''
6832 if (this.badge !== '') {
6833 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6839 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6842 a.cls += ' dropdown-toggle treeview' ;
6848 initEvents : function()
6850 if (typeof (this.menu) != 'undefined') {
6851 this.menu.parentType = this.xtype;
6852 this.menu.triggerEl = this.el;
6853 this.menu = this.addxtype(Roo.apply({}, this.menu));
6856 this.el.on('click', this.onClick, this);
6858 if(this.badge !== ''){
6859 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6864 onClick : function(e)
6871 if(this.preventDefault){
6875 this.fireEvent('click', this, e);
6878 disable : function()
6880 this.setDisabled(true);
6885 this.setDisabled(false);
6888 setDisabled : function(state)
6890 if(this.disabled == state){
6894 this.disabled = state;
6897 this.el.addClass('disabled');
6901 this.el.removeClass('disabled');
6906 setActive : function(state)
6908 if(this.active == state){
6912 this.active = state;
6915 this.el.addClass('active');
6919 this.el.removeClass('active');
6924 isActive: function ()
6929 setBadge : function(str)
6935 this.badgeEl.dom.innerHTML = str;
6950 Roo.namespace('Roo.bootstrap.breadcrumb');
6954 * @class Roo.bootstrap.breadcrumb.Nav
6955 * @extends Roo.bootstrap.Component
6956 * Bootstrap Breadcrumb Nav Class
6958 * @children Roo.bootstrap.breadcrumb.Item
6961 * Create a new breadcrumb.Nav
6962 * @param {Object} config The config object
6966 Roo.bootstrap.breadcrumb.Nav = function(config){
6967 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6972 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6974 getAutoCreate : function()
6991 initEvents: function()
6993 this.olEl = this.el.select('ol',true).first();
6995 getChildContainer : function()
7011 * @class Roo.bootstrap.breadcrumb.Nav
7012 * @extends Roo.bootstrap.Component
7013 * Bootstrap Breadcrumb Nav Class
7015 * @children Roo.bootstrap.breadcrumb.Component
7016 * @cfg {String} html the content of the link.
7017 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7018 * @cfg {Boolean} active is it active
7022 * Create a new breadcrumb.Nav
7023 * @param {Object} config The config object
7026 Roo.bootstrap.breadcrumb.Item = function(config){
7027 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7032 * The img click event for the img.
7033 * @param {Roo.EventObject} e
7040 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7045 getAutoCreate : function()
7050 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7052 if (this.href !== false) {
7059 cfg.html = this.html;
7065 initEvents: function()
7068 this.el.select('a', true).first().on('click',this.onClick, this)
7072 onClick : function(e)
7075 this.fireEvent('click',this, e);
7088 * @class Roo.bootstrap.Row
7089 * @extends Roo.bootstrap.Component
7090 * Bootstrap Row class (contains columns...)
7094 * @param {Object} config The config object
7097 Roo.bootstrap.Row = function(config){
7098 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7101 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7103 getAutoCreate : function(){
7122 * @class Roo.bootstrap.Pagination
7123 * @extends Roo.bootstrap.Component
7124 * Bootstrap Pagination class
7125 * @cfg {String} size xs | sm | md | lg
7126 * @cfg {Boolean} inverse false | true
7129 * Create a new Pagination
7130 * @param {Object} config The config object
7133 Roo.bootstrap.Pagination = function(config){
7134 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7137 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7143 getAutoCreate : function(){
7149 cfg.cls += ' inverse';
7155 cfg.cls += " " + this.cls;
7173 * @class Roo.bootstrap.PaginationItem
7174 * @extends Roo.bootstrap.Component
7175 * Bootstrap PaginationItem class
7176 * @cfg {String} html text
7177 * @cfg {String} href the link
7178 * @cfg {Boolean} preventDefault (true | false) default true
7179 * @cfg {Boolean} active (true | false) default false
7180 * @cfg {Boolean} disabled default false
7184 * Create a new PaginationItem
7185 * @param {Object} config The config object
7189 Roo.bootstrap.PaginationItem = function(config){
7190 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7195 * The raw click event for the entire grid.
7196 * @param {Roo.EventObject} e
7202 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7206 preventDefault: true,
7211 getAutoCreate : function(){
7217 href : this.href ? this.href : '#',
7218 html : this.html ? this.html : ''
7228 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7232 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7238 initEvents: function() {
7240 this.el.on('click', this.onClick, this);
7243 onClick : function(e)
7245 Roo.log('PaginationItem on click ');
7246 if(this.preventDefault){
7254 this.fireEvent('click', this, e);
7270 * @class Roo.bootstrap.Slider
7271 * @extends Roo.bootstrap.Component
7272 * Bootstrap Slider class
7275 * Create a new Slider
7276 * @param {Object} config The config object
7279 Roo.bootstrap.Slider = function(config){
7280 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7283 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7285 getAutoCreate : function(){
7289 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7293 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7305 * Ext JS Library 1.1.1
7306 * Copyright(c) 2006-2007, Ext JS, LLC.
7308 * Originally Released Under LGPL - original licence link has changed is not relivant.
7311 * <script type="text/javascript">
7314 * @extends Roo.dd.DDProxy
7315 * @class Roo.grid.SplitDragZone
7316 * Support for Column Header resizing
7318 * @param {Object} config
7321 // This is a support class used internally by the Grid components
7322 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7324 this.view = grid.getView();
7325 this.proxy = this.view.resizeProxy;
7326 Roo.grid.SplitDragZone.superclass.constructor.call(
7329 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7331 dragElId : Roo.id(this.proxy.dom),
7336 this.setHandleElId(Roo.id(hd));
7337 if (hd2 !== false) {
7338 this.setOuterHandleElId(Roo.id(hd2));
7341 this.scroll = false;
7343 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7344 fly: Roo.Element.fly,
7346 b4StartDrag : function(x, y){
7347 this.view.headersDisabled = true;
7348 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7349 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7351 this.proxy.setHeight(h);
7353 // for old system colWidth really stored the actual width?
7354 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7355 // which in reality did not work.. - it worked only for fixed sizes
7356 // for resizable we need to use actual sizes.
7357 var w = this.cm.getColumnWidth(this.cellIndex);
7358 if (!this.view.mainWrap) {
7360 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7365 // this was w-this.grid.minColumnWidth;
7366 // doesnt really make sense? - w = thie curren width or the rendered one?
7367 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7368 this.resetConstraints();
7369 this.setXConstraint(minw, 1000);
7370 this.setYConstraint(0, 0);
7371 this.minX = x - minw;
7372 this.maxX = x + 1000;
7374 if (!this.view.mainWrap) { // this is Bootstrap code..
7375 this.getDragEl().style.display='block';
7378 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7382 handleMouseDown : function(e){
7383 ev = Roo.EventObject.setEvent(e);
7384 var t = this.fly(ev.getTarget());
7385 if(t.hasClass("x-grid-split")){
7386 this.cellIndex = this.view.getCellIndex(t.dom);
7388 this.cm = this.grid.colModel;
7389 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7390 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7395 endDrag : function(e){
7396 this.view.headersDisabled = false;
7397 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7398 var diff = endX - this.startPos;
7400 var w = this.cm.getColumnWidth(this.cellIndex);
7401 if (!this.view.mainWrap) {
7404 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7407 autoOffset : function(){
7412 * Ext JS Library 1.1.1
7413 * Copyright(c) 2006-2007, Ext JS, LLC.
7415 * Originally Released Under LGPL - original licence link has changed is not relivant.
7418 * <script type="text/javascript">
7422 * @class Roo.grid.AbstractSelectionModel
7423 * @extends Roo.util.Observable
7425 * Abstract base class for grid SelectionModels. It provides the interface that should be
7426 * implemented by descendant classes. This class should not be directly instantiated.
7429 Roo.grid.AbstractSelectionModel = function(){
7430 this.locked = false;
7431 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7434 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7435 /** @ignore Called by the grid automatically. Do not call directly. */
7436 init : function(grid){
7442 * Locks the selections.
7449 * Unlocks the selections.
7451 unlock : function(){
7452 this.locked = false;
7456 * Returns true if the selections are locked.
7459 isLocked : function(){
7464 * Ext JS Library 1.1.1
7465 * Copyright(c) 2006-2007, Ext JS, LLC.
7467 * Originally Released Under LGPL - original licence link has changed is not relivant.
7470 * <script type="text/javascript">
7473 * @extends Roo.grid.AbstractSelectionModel
7474 * @class Roo.grid.RowSelectionModel
7475 * The default SelectionModel used by {@link Roo.grid.Grid}.
7476 * It supports multiple selections and keyboard selection/navigation.
7478 * @param {Object} config
7480 Roo.grid.RowSelectionModel = function(config){
7481 Roo.apply(this, config);
7482 this.selections = new Roo.util.MixedCollection(false, function(o){
7487 this.lastActive = false;
7491 * @event selectionchange
7492 * Fires when the selection changes
7493 * @param {SelectionModel} this
7495 "selectionchange" : true,
7497 * @event afterselectionchange
7498 * Fires after the selection changes (eg. by key press or clicking)
7499 * @param {SelectionModel} this
7501 "afterselectionchange" : true,
7503 * @event beforerowselect
7504 * Fires when a row is selected being selected, return false to cancel.
7505 * @param {SelectionModel} this
7506 * @param {Number} rowIndex The selected index
7507 * @param {Boolean} keepExisting False if other selections will be cleared
7509 "beforerowselect" : true,
7512 * Fires when a row is selected.
7513 * @param {SelectionModel} this
7514 * @param {Number} rowIndex The selected index
7515 * @param {Roo.data.Record} r The record
7519 * @event rowdeselect
7520 * Fires when a row is deselected.
7521 * @param {SelectionModel} this
7522 * @param {Number} rowIndex The selected index
7524 "rowdeselect" : true
7526 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7527 this.locked = false;
7530 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7532 * @cfg {Boolean} singleSelect
7533 * True to allow selection of only one row at a time (defaults to false)
7535 singleSelect : false,
7538 initEvents : function(){
7540 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7541 this.grid.on("mousedown", this.handleMouseDown, this);
7542 }else{ // allow click to work like normal
7543 this.grid.on("rowclick", this.handleDragableRowClick, this);
7545 // bootstrap does not have a view..
7546 var view = this.grid.view ? this.grid.view : this.grid;
7547 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7550 this.selectPrevious(e.shiftKey);
7551 }else if(this.last !== false && this.lastActive !== false){
7552 var last = this.last;
7553 this.selectRange(this.last, this.lastActive-1);
7554 view.focusRow(this.lastActive);
7559 this.selectFirstRow();
7561 this.fireEvent("afterselectionchange", this);
7563 "down" : function(e){
7565 this.selectNext(e.shiftKey);
7566 }else if(this.last !== false && this.lastActive !== false){
7567 var last = this.last;
7568 this.selectRange(this.last, this.lastActive+1);
7569 view.focusRow(this.lastActive);
7574 this.selectFirstRow();
7576 this.fireEvent("afterselectionchange", this);
7582 view.on("refresh", this.onRefresh, this);
7583 view.on("rowupdated", this.onRowUpdated, this);
7584 view.on("rowremoved", this.onRemove, this);
7588 onRefresh : function(){
7589 var ds = this.grid.ds, i, v = this.grid.view;
7590 var s = this.selections;
7592 if((i = ds.indexOfId(r.id)) != -1){
7594 s.add(ds.getAt(i)); // updating the selection relate data
7602 onRemove : function(v, index, r){
7603 this.selections.remove(r);
7607 onRowUpdated : function(v, index, r){
7608 if(this.isSelected(r)){
7609 v.onRowSelect(index);
7615 * @param {Array} records The records to select
7616 * @param {Boolean} keepExisting (optional) True to keep existing selections
7618 selectRecords : function(records, keepExisting){
7620 this.clearSelections();
7622 var ds = this.grid.ds;
7623 for(var i = 0, len = records.length; i < len; i++){
7624 this.selectRow(ds.indexOf(records[i]), true);
7629 * Gets the number of selected rows.
7632 getCount : function(){
7633 return this.selections.length;
7637 * Selects the first row in the grid.
7639 selectFirstRow : function(){
7644 * Select the last row.
7645 * @param {Boolean} keepExisting (optional) True to keep existing selections
7647 selectLastRow : function(keepExisting){
7648 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7652 * Selects the row immediately following the last selected row.
7653 * @param {Boolean} keepExisting (optional) True to keep existing selections
7655 selectNext : function(keepExisting){
7656 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7657 this.selectRow(this.last+1, keepExisting);
7658 var view = this.grid.view ? this.grid.view : this.grid;
7659 view.focusRow(this.last);
7664 * Selects the row that precedes the last selected row.
7665 * @param {Boolean} keepExisting (optional) True to keep existing selections
7667 selectPrevious : function(keepExisting){
7669 this.selectRow(this.last-1, keepExisting);
7670 var view = this.grid.view ? this.grid.view : this.grid;
7671 view.focusRow(this.last);
7676 * Returns the selected records
7677 * @return {Array} Array of selected records
7679 getSelections : function(){
7680 return [].concat(this.selections.items);
7684 * Returns the first selected record.
7687 getSelected : function(){
7688 return this.selections.itemAt(0);
7693 * Clears all selections.
7695 clearSelections : function(fast){
7700 var ds = this.grid.ds;
7701 var s = this.selections;
7703 this.deselectRow(ds.indexOfId(r.id));
7707 this.selections.clear();
7716 selectAll : function(){
7720 this.selections.clear();
7721 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7722 this.selectRow(i, true);
7727 * Returns True if there is a selection.
7730 hasSelection : function(){
7731 return this.selections.length > 0;
7735 * Returns True if the specified row is selected.
7736 * @param {Number/Record} record The record or index of the record to check
7739 isSelected : function(index){
7740 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7741 return (r && this.selections.key(r.id) ? true : false);
7745 * Returns True if the specified record id is selected.
7746 * @param {String} id The id of record to check
7749 isIdSelected : function(id){
7750 return (this.selections.key(id) ? true : false);
7754 handleMouseDown : function(e, t)
7756 var view = this.grid.view ? this.grid.view : this.grid;
7758 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7761 if(e.shiftKey && this.last !== false){
7762 var last = this.last;
7763 this.selectRange(last, rowIndex, e.ctrlKey);
7764 this.last = last; // reset the last
7765 view.focusRow(rowIndex);
7767 var isSelected = this.isSelected(rowIndex);
7768 if(e.button !== 0 && isSelected){
7769 view.focusRow(rowIndex);
7770 }else if(e.ctrlKey && isSelected){
7771 this.deselectRow(rowIndex);
7772 }else if(!isSelected){
7773 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7774 view.focusRow(rowIndex);
7777 this.fireEvent("afterselectionchange", this);
7780 handleDragableRowClick : function(grid, rowIndex, e)
7782 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7783 this.selectRow(rowIndex, false);
7784 var view = this.grid.view ? this.grid.view : this.grid;
7785 view.focusRow(rowIndex);
7786 this.fireEvent("afterselectionchange", this);
7791 * Selects multiple rows.
7792 * @param {Array} rows Array of the indexes of the row to select
7793 * @param {Boolean} keepExisting (optional) True to keep existing selections
7795 selectRows : function(rows, keepExisting){
7797 this.clearSelections();
7799 for(var i = 0, len = rows.length; i < len; i++){
7800 this.selectRow(rows[i], true);
7805 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7806 * @param {Number} startRow The index of the first row in the range
7807 * @param {Number} endRow The index of the last row in the range
7808 * @param {Boolean} keepExisting (optional) True to retain existing selections
7810 selectRange : function(startRow, endRow, keepExisting){
7815 this.clearSelections();
7817 if(startRow <= endRow){
7818 for(var i = startRow; i <= endRow; i++){
7819 this.selectRow(i, true);
7822 for(var i = startRow; i >= endRow; i--){
7823 this.selectRow(i, true);
7829 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7830 * @param {Number} startRow The index of the first row in the range
7831 * @param {Number} endRow The index of the last row in the range
7833 deselectRange : function(startRow, endRow, preventViewNotify){
7837 for(var i = startRow; i <= endRow; i++){
7838 this.deselectRow(i, preventViewNotify);
7844 * @param {Number} row The index of the row to select
7845 * @param {Boolean} keepExisting (optional) True to keep existing selections
7847 selectRow : function(index, keepExisting, preventViewNotify){
7848 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7851 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7852 if(!keepExisting || this.singleSelect){
7853 this.clearSelections();
7855 var r = this.grid.ds.getAt(index);
7856 this.selections.add(r);
7857 this.last = this.lastActive = index;
7858 if(!preventViewNotify){
7859 var view = this.grid.view ? this.grid.view : this.grid;
7860 view.onRowSelect(index);
7862 this.fireEvent("rowselect", this, index, r);
7863 this.fireEvent("selectionchange", this);
7869 * @param {Number} row The index of the row to deselect
7871 deselectRow : function(index, preventViewNotify){
7875 if(this.last == index){
7878 if(this.lastActive == index){
7879 this.lastActive = false;
7881 var r = this.grid.ds.getAt(index);
7882 this.selections.remove(r);
7883 if(!preventViewNotify){
7884 var view = this.grid.view ? this.grid.view : this.grid;
7885 view.onRowDeselect(index);
7887 this.fireEvent("rowdeselect", this, index);
7888 this.fireEvent("selectionchange", this);
7892 restoreLast : function(){
7894 this.last = this._last;
7899 acceptsNav : function(row, col, cm){
7900 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7904 onEditorKey : function(field, e){
7905 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7910 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7912 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7914 }else if(k == e.ENTER && !e.ctrlKey){
7918 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7920 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7922 }else if(k == e.ESC){
7926 g.startEditing(newCell[0], newCell[1]);
7931 * Ext JS Library 1.1.1
7932 * Copyright(c) 2006-2007, Ext JS, LLC.
7934 * Originally Released Under LGPL - original licence link has changed is not relivant.
7937 * <script type="text/javascript">
7942 * @class Roo.grid.ColumnModel
7943 * @extends Roo.util.Observable
7944 * This is the default implementation of a ColumnModel used by the Grid. It defines
7945 * the columns in the grid.
7948 var colModel = new Roo.grid.ColumnModel([
7949 {header: "Ticker", width: 60, sortable: true, locked: true},
7950 {header: "Company Name", width: 150, sortable: true},
7951 {header: "Market Cap.", width: 100, sortable: true},
7952 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7953 {header: "Employees", width: 100, sortable: true, resizable: false}
7958 * The config options listed for this class are options which may appear in each
7959 * individual column definition.
7960 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7962 * @param {Object} config An Array of column config objects. See this class's
7963 * config objects for details.
7965 Roo.grid.ColumnModel = function(config){
7967 * The config passed into the constructor
7969 this.config = []; //config;
7972 // if no id, create one
7973 // if the column does not have a dataIndex mapping,
7974 // map it to the order it is in the config
7975 for(var i = 0, len = config.length; i < len; i++){
7976 this.addColumn(config[i]);
7981 * The width of columns which have no width specified (defaults to 100)
7984 this.defaultWidth = 100;
7987 * Default sortable of columns which have no sortable specified (defaults to false)
7990 this.defaultSortable = false;
7994 * @event widthchange
7995 * Fires when the width of a column changes.
7996 * @param {ColumnModel} this
7997 * @param {Number} columnIndex The column index
7998 * @param {Number} newWidth The new width
8000 "widthchange": true,
8002 * @event headerchange
8003 * Fires when the text of a header changes.
8004 * @param {ColumnModel} this
8005 * @param {Number} columnIndex The column index
8006 * @param {Number} newText The new header text
8008 "headerchange": true,
8010 * @event hiddenchange
8011 * Fires when a column is hidden or "unhidden".
8012 * @param {ColumnModel} this
8013 * @param {Number} columnIndex The column index
8014 * @param {Boolean} hidden true if hidden, false otherwise
8016 "hiddenchange": true,
8018 * @event columnmoved
8019 * Fires when a column is moved.
8020 * @param {ColumnModel} this
8021 * @param {Number} oldIndex
8022 * @param {Number} newIndex
8024 "columnmoved" : true,
8026 * @event columlockchange
8027 * Fires when a column's locked state is changed
8028 * @param {ColumnModel} this
8029 * @param {Number} colIndex
8030 * @param {Boolean} locked true if locked
8032 "columnlockchange" : true
8034 Roo.grid.ColumnModel.superclass.constructor.call(this);
8036 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8038 * @cfg {String} header The header text to display in the Grid view.
8041 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8044 * @cfg {String} smHeader Header at Bootsrap Small width
8047 * @cfg {String} mdHeader Header at Bootsrap Medium width
8050 * @cfg {String} lgHeader Header at Bootsrap Large width
8053 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8056 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8057 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8058 * specified, the column's index is used as an index into the Record's data Array.
8061 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8062 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8065 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8066 * Defaults to the value of the {@link #defaultSortable} property.
8067 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8070 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8073 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8076 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8079 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8082 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8083 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8084 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8085 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8088 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8091 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8094 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8097 * @cfg {String} cursor (Optional)
8100 * @cfg {String} tooltip (Optional)
8103 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8106 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8109 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8112 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8115 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8118 * Returns the id of the column at the specified index.
8119 * @param {Number} index The column index
8120 * @return {String} the id
8122 getColumnId : function(index){
8123 return this.config[index].id;
8127 * Returns the column for a specified id.
8128 * @param {String} id The column id
8129 * @return {Object} the column
8131 getColumnById : function(id){
8132 return this.lookup[id];
8137 * Returns the column Object for a specified dataIndex.
8138 * @param {String} dataIndex The column dataIndex
8139 * @return {Object|Boolean} the column or false if not found
8141 getColumnByDataIndex: function(dataIndex){
8142 var index = this.findColumnIndex(dataIndex);
8143 return index > -1 ? this.config[index] : false;
8147 * Returns the index for a specified column id.
8148 * @param {String} id The column id
8149 * @return {Number} the index, or -1 if not found
8151 getIndexById : function(id){
8152 for(var i = 0, len = this.config.length; i < len; i++){
8153 if(this.config[i].id == id){
8161 * Returns the index for a specified column dataIndex.
8162 * @param {String} dataIndex The column dataIndex
8163 * @return {Number} the index, or -1 if not found
8166 findColumnIndex : function(dataIndex){
8167 for(var i = 0, len = this.config.length; i < len; i++){
8168 if(this.config[i].dataIndex == dataIndex){
8176 moveColumn : function(oldIndex, newIndex){
8177 var c = this.config[oldIndex];
8178 this.config.splice(oldIndex, 1);
8179 this.config.splice(newIndex, 0, c);
8180 this.dataMap = null;
8181 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8184 isLocked : function(colIndex){
8185 return this.config[colIndex].locked === true;
8188 setLocked : function(colIndex, value, suppressEvent){
8189 if(this.isLocked(colIndex) == value){
8192 this.config[colIndex].locked = value;
8194 this.fireEvent("columnlockchange", this, colIndex, value);
8198 getTotalLockedWidth : function(){
8200 for(var i = 0; i < this.config.length; i++){
8201 if(this.isLocked(i) && !this.isHidden(i)){
8202 this.totalWidth += this.getColumnWidth(i);
8208 getLockedCount : function(){
8209 for(var i = 0, len = this.config.length; i < len; i++){
8210 if(!this.isLocked(i)){
8215 return this.config.length;
8219 * Returns the number of columns.
8222 getColumnCount : function(visibleOnly){
8223 if(visibleOnly === true){
8225 for(var i = 0, len = this.config.length; i < len; i++){
8226 if(!this.isHidden(i)){
8232 return this.config.length;
8236 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8237 * @param {Function} fn
8238 * @param {Object} scope (optional)
8239 * @return {Array} result
8241 getColumnsBy : function(fn, scope){
8243 for(var i = 0, len = this.config.length; i < len; i++){
8244 var c = this.config[i];
8245 if(fn.call(scope||this, c, i) === true){
8253 * Returns true if the specified column is sortable.
8254 * @param {Number} col The column index
8257 isSortable : function(col){
8258 if(typeof this.config[col].sortable == "undefined"){
8259 return this.defaultSortable;
8261 return this.config[col].sortable;
8265 * Returns the rendering (formatting) function defined for the column.
8266 * @param {Number} col The column index.
8267 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8269 getRenderer : function(col){
8270 if(!this.config[col].renderer){
8271 return Roo.grid.ColumnModel.defaultRenderer;
8273 return this.config[col].renderer;
8277 * Sets the rendering (formatting) function for a column.
8278 * @param {Number} col The column index
8279 * @param {Function} fn The function to use to process the cell's raw data
8280 * to return HTML markup for the grid view. The render function is called with
8281 * the following parameters:<ul>
8282 * <li>Data value.</li>
8283 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8284 * <li>css A CSS style string to apply to the table cell.</li>
8285 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8286 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8287 * <li>Row index</li>
8288 * <li>Column index</li>
8289 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8291 setRenderer : function(col, fn){
8292 this.config[col].renderer = fn;
8296 * Returns the width for the specified column.
8297 * @param {Number} col The column index
8298 * @param (optional) {String} gridSize bootstrap width size.
8301 getColumnWidth : function(col, gridSize)
8303 var cfg = this.config[col];
8305 if (typeof(gridSize) == 'undefined') {
8306 return cfg.width * 1 || this.defaultWidth;
8308 if (gridSize === false) { // if we set it..
8309 return cfg.width || false;
8311 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8313 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8314 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8317 return cfg[ sizes[i] ];
8324 * Sets the width for a column.
8325 * @param {Number} col The column index
8326 * @param {Number} width The new width
8328 setColumnWidth : function(col, width, suppressEvent){
8329 this.config[col].width = width;
8330 this.totalWidth = null;
8332 this.fireEvent("widthchange", this, col, width);
8337 * Returns the total width of all columns.
8338 * @param {Boolean} includeHidden True to include hidden column widths
8341 getTotalWidth : function(includeHidden){
8342 if(!this.totalWidth){
8343 this.totalWidth = 0;
8344 for(var i = 0, len = this.config.length; i < len; i++){
8345 if(includeHidden || !this.isHidden(i)){
8346 this.totalWidth += this.getColumnWidth(i);
8350 return this.totalWidth;
8354 * Returns the header for the specified column.
8355 * @param {Number} col The column index
8358 getColumnHeader : function(col){
8359 return this.config[col].header;
8363 * Sets the header for a column.
8364 * @param {Number} col The column index
8365 * @param {String} header The new header
8367 setColumnHeader : function(col, header){
8368 this.config[col].header = header;
8369 this.fireEvent("headerchange", this, col, header);
8373 * Returns the tooltip for the specified column.
8374 * @param {Number} col The column index
8377 getColumnTooltip : function(col){
8378 return this.config[col].tooltip;
8381 * Sets the tooltip for a column.
8382 * @param {Number} col The column index
8383 * @param {String} tooltip The new tooltip
8385 setColumnTooltip : function(col, tooltip){
8386 this.config[col].tooltip = tooltip;
8390 * Returns the dataIndex for the specified column.
8391 * @param {Number} col The column index
8394 getDataIndex : function(col){
8395 return this.config[col].dataIndex;
8399 * Sets the dataIndex for a column.
8400 * @param {Number} col The column index
8401 * @param {Number} dataIndex The new dataIndex
8403 setDataIndex : function(col, dataIndex){
8404 this.config[col].dataIndex = dataIndex;
8410 * Returns true if the cell is editable.
8411 * @param {Number} colIndex The column index
8412 * @param {Number} rowIndex The row index - this is nto actually used..?
8415 isCellEditable : function(colIndex, rowIndex){
8416 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8420 * Returns the editor defined for the cell/column.
8421 * return false or null to disable editing.
8422 * @param {Number} colIndex The column index
8423 * @param {Number} rowIndex The row index
8426 getCellEditor : function(colIndex, rowIndex){
8427 return this.config[colIndex].editor;
8431 * Sets if a column is editable.
8432 * @param {Number} col The column index
8433 * @param {Boolean} editable True if the column is editable
8435 setEditable : function(col, editable){
8436 this.config[col].editable = editable;
8441 * Returns true if the column is hidden.
8442 * @param {Number} colIndex The column index
8445 isHidden : function(colIndex){
8446 return this.config[colIndex].hidden;
8451 * Returns true if the column width cannot be changed
8453 isFixed : function(colIndex){
8454 return this.config[colIndex].fixed;
8458 * Returns true if the column can be resized
8461 isResizable : function(colIndex){
8462 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8465 * Sets if a column is hidden.
8466 * @param {Number} colIndex The column index
8467 * @param {Boolean} hidden True if the column is hidden
8469 setHidden : function(colIndex, hidden){
8470 this.config[colIndex].hidden = hidden;
8471 this.totalWidth = null;
8472 this.fireEvent("hiddenchange", this, colIndex, hidden);
8476 * Sets the editor for a column.
8477 * @param {Number} col The column index
8478 * @param {Object} editor The editor object
8480 setEditor : function(col, editor){
8481 this.config[col].editor = editor;
8484 * Add a column (experimental...) - defaults to adding to the end..
8485 * @param {Object} config
8487 addColumn : function(c)
8490 var i = this.config.length;
8493 if(typeof c.dataIndex == "undefined"){
8496 if(typeof c.renderer == "string"){
8497 c.renderer = Roo.util.Format[c.renderer];
8499 if(typeof c.id == "undefined"){
8502 if(c.editor && c.editor.xtype){
8503 c.editor = Roo.factory(c.editor, Roo.grid);
8505 if(c.editor && c.editor.isFormField){
8506 c.editor = new Roo.grid.GridEditor(c.editor);
8508 this.lookup[c.id] = c;
8513 Roo.grid.ColumnModel.defaultRenderer = function(value)
8515 if(typeof value == "object") {
8518 if(typeof value == "string" && value.length < 1){
8522 return String.format("{0}", value);
8525 // Alias for backwards compatibility
8526 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8529 * Ext JS Library 1.1.1
8530 * Copyright(c) 2006-2007, Ext JS, LLC.
8532 * Originally Released Under LGPL - original licence link has changed is not relivant.
8535 * <script type="text/javascript">
8539 * @class Roo.LoadMask
8540 * A simple utility class for generically masking elements while loading data. If the element being masked has
8541 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8542 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8543 * element's UpdateManager load indicator and will be destroyed after the initial load.
8545 * Create a new LoadMask
8546 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8547 * @param {Object} config The config object
8549 Roo.LoadMask = function(el, config){
8550 this.el = Roo.get(el);
8551 Roo.apply(this, config);
8553 this.store.on('beforeload', this.onBeforeLoad, this);
8554 this.store.on('load', this.onLoad, this);
8555 this.store.on('loadexception', this.onLoadException, this);
8556 this.removeMask = false;
8558 var um = this.el.getUpdateManager();
8559 um.showLoadIndicator = false; // disable the default indicator
8560 um.on('beforeupdate', this.onBeforeLoad, this);
8561 um.on('update', this.onLoad, this);
8562 um.on('failure', this.onLoad, this);
8563 this.removeMask = true;
8567 Roo.LoadMask.prototype = {
8569 * @cfg {Boolean} removeMask
8570 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8571 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8576 * The text to display in a centered loading message box (defaults to 'Loading...')
8580 * @cfg {String} msgCls
8581 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8583 msgCls : 'x-mask-loading',
8586 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8592 * Disables the mask to prevent it from being displayed
8594 disable : function(){
8595 this.disabled = true;
8599 * Enables the mask so that it can be displayed
8601 enable : function(){
8602 this.disabled = false;
8605 onLoadException : function()
8609 if (typeof(arguments[3]) != 'undefined') {
8610 Roo.MessageBox.alert("Error loading",arguments[3]);
8614 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8615 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8622 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8627 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8631 onBeforeLoad : function(){
8633 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8638 destroy : function(){
8640 this.store.un('beforeload', this.onBeforeLoad, this);
8641 this.store.un('load', this.onLoad, this);
8642 this.store.un('loadexception', this.onLoadException, this);
8644 var um = this.el.getUpdateManager();
8645 um.un('beforeupdate', this.onBeforeLoad, this);
8646 um.un('update', this.onLoad, this);
8647 um.un('failure', this.onLoad, this);
8651 * @class Roo.bootstrap.Table
8653 * @extends Roo.bootstrap.Component
8654 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8655 * Similar to Roo.grid.Grid
8657 var table = Roo.factory({
8659 xns : Roo.bootstrap,
8660 autoSizeColumns: true,
8667 sortInfo : { direction : 'ASC', field: 'name' },
8669 xtype : 'HttpProxy',
8672 url : 'https://example.com/some.data.url.json'
8675 xtype : 'JsonReader',
8677 fields : [ 'id', 'name', whatever' ],
8684 xtype : 'ColumnModel',
8688 dataIndex : 'is_in_group',
8691 renderer : function(v, x , r) {
8693 return String.format("{0}", v)
8699 xtype : 'RowSelectionModel',
8700 xns : Roo.bootstrap.Table
8701 // you can add listeners to catch selection change here....
8707 grid.render(Roo.get("some-div"));
8710 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8715 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8716 * @cfg {Roo.data.Store} store The data store to use
8717 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8719 * @cfg {String} cls table class
8722 * @cfg {boolean} striped Should the rows be alternative striped
8723 * @cfg {boolean} bordered Add borders to the table
8724 * @cfg {boolean} hover Add hover highlighting
8725 * @cfg {boolean} condensed Format condensed
8726 * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8727 * also adds table-responsive (see bootstrap docs for details)
8728 * @cfg {Boolean} loadMask (true|false) default false
8729 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8730 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8731 * @cfg {Boolean} rowSelection (true|false) default false
8732 * @cfg {Boolean} cellSelection (true|false) default false
8733 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8734 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8735 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8736 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8737 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8738 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8741 * Create a new Table
8742 * @param {Object} config The config object
8745 Roo.bootstrap.Table = function(config)
8747 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8750 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8751 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8752 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8753 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8755 this.view = this; // compat with grid.
8757 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8759 this.sm.grid = this;
8760 this.selModel = Roo.factory(this.sm, Roo.grid);
8761 this.sm = this.selModel;
8762 this.sm.xmodule = this.xmodule || false;
8765 if (this.cm && typeof(this.cm.config) == 'undefined') {
8766 this.colModel = new Roo.grid.ColumnModel(this.cm);
8767 this.cm = this.colModel;
8768 this.cm.xmodule = this.xmodule || false;
8771 this.store= Roo.factory(this.store, Roo.data);
8772 this.ds = this.store;
8773 this.ds.xmodule = this.xmodule || false;
8776 if (this.footer && this.store) {
8777 this.footer.dataSource = this.ds;
8778 this.footer = Roo.factory(this.footer);
8785 * Fires when a cell is clicked
8786 * @param {Roo.bootstrap.Table} this
8787 * @param {Roo.Element} el
8788 * @param {Number} rowIndex
8789 * @param {Number} columnIndex
8790 * @param {Roo.EventObject} e
8794 * @event celldblclick
8795 * Fires when a cell is double clicked
8796 * @param {Roo.bootstrap.Table} this
8797 * @param {Roo.Element} el
8798 * @param {Number} rowIndex
8799 * @param {Number} columnIndex
8800 * @param {Roo.EventObject} e
8802 "celldblclick" : true,
8805 * Fires when a row is clicked
8806 * @param {Roo.bootstrap.Table} this
8807 * @param {Roo.Element} el
8808 * @param {Number} rowIndex
8809 * @param {Roo.EventObject} e
8813 * @event rowdblclick
8814 * Fires when a row is double clicked
8815 * @param {Roo.bootstrap.Table} this
8816 * @param {Roo.Element} el
8817 * @param {Number} rowIndex
8818 * @param {Roo.EventObject} e
8820 "rowdblclick" : true,
8823 * Fires when a mouseover occur
8824 * @param {Roo.bootstrap.Table} this
8825 * @param {Roo.Element} el
8826 * @param {Number} rowIndex
8827 * @param {Number} columnIndex
8828 * @param {Roo.EventObject} e
8833 * Fires when a mouseout occur
8834 * @param {Roo.bootstrap.Table} this
8835 * @param {Roo.Element} el
8836 * @param {Number} rowIndex
8837 * @param {Number} columnIndex
8838 * @param {Roo.EventObject} e
8843 * Fires when a row is rendered, so you can change add a style to it.
8844 * @param {Roo.bootstrap.Table} this
8845 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8849 * @event rowsrendered
8850 * Fires when all the rows have been rendered
8851 * @param {Roo.bootstrap.Table} this
8853 'rowsrendered' : true,
8855 * @event contextmenu
8856 * The raw contextmenu event for the entire grid.
8857 * @param {Roo.EventObject} e
8859 "contextmenu" : true,
8861 * @event rowcontextmenu
8862 * Fires when a row is right clicked
8863 * @param {Roo.bootstrap.Table} this
8864 * @param {Number} rowIndex
8865 * @param {Roo.EventObject} e
8867 "rowcontextmenu" : true,
8869 * @event cellcontextmenu
8870 * Fires when a cell is right clicked
8871 * @param {Roo.bootstrap.Table} this
8872 * @param {Number} rowIndex
8873 * @param {Number} cellIndex
8874 * @param {Roo.EventObject} e
8876 "cellcontextmenu" : true,
8878 * @event headercontextmenu
8879 * Fires when a header is right clicked
8880 * @param {Roo.bootstrap.Table} this
8881 * @param {Number} columnIndex
8882 * @param {Roo.EventObject} e
8884 "headercontextmenu" : true,
8887 * The raw mousedown event for the entire grid.
8888 * @param {Roo.EventObject} e
8895 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8911 enableColumnResize: true,
8913 rowSelection : false,
8914 cellSelection : false,
8917 minColumnWidth : 50,
8919 // Roo.Element - the tbody
8920 bodyEl: false, // <tbody> Roo.Element - thead element
8921 headEl: false, // <thead> Roo.Element - thead element
8922 resizeProxy : false, // proxy element for dragging?
8926 container: false, // used by gridpanel...
8932 auto_hide_footer : false,
8934 view: false, // actually points to this..
8936 getAutoCreate : function()
8938 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8945 // this get's auto added by panel.Grid
8946 if (this.scrollBody) {
8947 cfg.cls += ' table-body-fixed';
8950 cfg.cls += ' table-striped';
8954 cfg.cls += ' table-hover';
8956 if (this.bordered) {
8957 cfg.cls += ' table-bordered';
8959 if (this.condensed) {
8960 cfg.cls += ' table-condensed';
8963 if (this.responsive) {
8964 cfg.cls += ' table-responsive';
8968 cfg.cls+= ' ' +this.cls;
8974 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8977 if(this.store || this.cm){
8978 if(this.headerShow){
8979 cfg.cn.push(this.renderHeader());
8982 cfg.cn.push(this.renderBody());
8984 if(this.footerShow){
8985 cfg.cn.push(this.renderFooter());
8987 // where does this come from?
8988 //cfg.cls+= ' TableGrid';
8991 return { cn : [ cfg ] };
8994 initEvents : function()
8996 if(!this.store || !this.cm){
8999 if (this.selModel) {
9000 this.selModel.initEvents();
9004 //Roo.log('initEvents with ds!!!!');
9006 this.bodyEl = this.el.select('tbody', true).first();
9007 this.headEl = this.el.select('thead', true).first();
9008 this.mainFoot = this.el.select('tfoot', true).first();
9013 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9014 e.on('click', this.sort, this);
9018 // why is this done????? = it breaks dialogs??
9019 //this.parent().el.setStyle('position', 'relative');
9023 this.footer.parentId = this.id;
9024 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9027 this.el.select('tfoot tr td').first().addClass('hide');
9032 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9035 this.store.on('load', this.onLoad, this);
9036 this.store.on('beforeload', this.onBeforeLoad, this);
9037 this.store.on('update', this.onUpdate, this);
9038 this.store.on('add', this.onAdd, this);
9039 this.store.on("clear", this.clear, this);
9041 this.el.on("contextmenu", this.onContextMenu, this);
9044 this.cm.on("headerchange", this.onHeaderChange, this);
9045 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9047 //?? does bodyEl get replaced on render?
9048 this.bodyEl.on("click", this.onClick, this);
9049 this.bodyEl.on("dblclick", this.onDblClick, this);
9050 this.bodyEl.on('scroll', this.onBodyScroll, this);
9052 // guessing mainbody will work - this relays usually caught by selmodel at present.
9053 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9056 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9059 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9060 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9065 // Compatibility with grid - we implement all the view features at present.
9066 getView : function()
9071 initCSS : function()
9075 var cm = this.cm, styles = [];
9076 this.CSS.removeStyleSheet(this.id + '-cssrules');
9077 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9078 // we can honour xs/sm/md/xl as widths...
9079 // we first have to decide what widht we are currently at...
9080 var sz = Roo.getGridSize();
9084 var cols = []; // visable cols.
9086 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9087 var w = cm.getColumnWidth(i, false);
9089 cols.push( { rel : false, abs : 0 });
9093 cols.push( { rel : false, abs : w });
9095 last = i; // not really..
9098 var w = cm.getColumnWidth(i, sz);
9103 cols.push( { rel : w, abs : false });
9106 var avail = this.bodyEl.dom.clientWidth - total_abs;
9108 var unitWidth = Math.floor(avail / total);
9109 var rem = avail - (unitWidth * total);
9111 var hidden, width, pos = 0 , splithide , left;
9112 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9114 hidden = 'display:none;';
9116 width = 'width:0px;';
9118 if(!cm.isHidden(i)){
9122 // we can honour xs/sm/md/xl ?
9123 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9125 hidden = 'display:none;';
9127 // width should return a small number...
9129 w+=rem; // add the remaining with..
9132 left = "left:" + (pos -4) + "px;";
9133 width = "width:" + w+ "px;";
9136 if (this.responsive) {
9139 hidden = cm.isHidden(i) ? 'display:none;' : '';
9140 splithide = 'display: none;';
9143 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9146 splithide = 'display:none;';
9149 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9150 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9155 //Roo.log(styles.join(''));
9156 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9162 onContextMenu : function(e, t)
9164 this.processEvent("contextmenu", e);
9167 processEvent : function(name, e)
9169 if (name != 'touchstart' ) {
9170 this.fireEvent(name, e);
9173 var t = e.getTarget();
9175 var cell = Roo.get(t);
9181 if(cell.findParent('tfoot', false, true)){
9185 if(cell.findParent('thead', false, true)){
9187 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9188 cell = Roo.get(t).findParent('th', false, true);
9190 Roo.log("failed to find th in thead?");
9191 Roo.log(e.getTarget());
9196 var cellIndex = cell.dom.cellIndex;
9198 var ename = name == 'touchstart' ? 'click' : name;
9199 this.fireEvent("header" + ename, this, cellIndex, e);
9204 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9205 cell = Roo.get(t).findParent('td', false, true);
9207 Roo.log("failed to find th in tbody?");
9208 Roo.log(e.getTarget());
9213 var row = cell.findParent('tr', false, true);
9214 var cellIndex = cell.dom.cellIndex;
9215 var rowIndex = row.dom.rowIndex - 1;
9219 this.fireEvent("row" + name, this, rowIndex, e);
9223 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9229 onMouseover : function(e, el)
9231 var cell = Roo.get(el);
9237 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9238 cell = cell.findParent('td', false, true);
9241 var row = cell.findParent('tr', false, true);
9242 var cellIndex = cell.dom.cellIndex;
9243 var rowIndex = row.dom.rowIndex - 1; // start from 0
9245 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9249 onMouseout : function(e, el)
9251 var cell = Roo.get(el);
9257 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9258 cell = cell.findParent('td', false, true);
9261 var row = cell.findParent('tr', false, true);
9262 var cellIndex = cell.dom.cellIndex;
9263 var rowIndex = row.dom.rowIndex - 1; // start from 0
9265 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9269 onClick : function(e, el)
9271 var cell = Roo.get(el);
9273 if(!cell || (!this.cellSelection && !this.rowSelection)){
9277 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9278 cell = cell.findParent('td', false, true);
9281 if(!cell || typeof(cell) == 'undefined'){
9285 var row = cell.findParent('tr', false, true);
9287 if(!row || typeof(row) == 'undefined'){
9291 var cellIndex = cell.dom.cellIndex;
9292 var rowIndex = this.getRowIndex(row);
9294 // why??? - should these not be based on SelectionModel?
9295 //if(this.cellSelection){
9296 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9299 //if(this.rowSelection){
9300 this.fireEvent('rowclick', this, row, rowIndex, e);
9305 onDblClick : function(e,el)
9307 var cell = Roo.get(el);
9309 if(!cell || (!this.cellSelection && !this.rowSelection)){
9313 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9314 cell = cell.findParent('td', false, true);
9317 if(!cell || typeof(cell) == 'undefined'){
9321 var row = cell.findParent('tr', false, true);
9323 if(!row || typeof(row) == 'undefined'){
9327 var cellIndex = cell.dom.cellIndex;
9328 var rowIndex = this.getRowIndex(row);
9330 if(this.cellSelection){
9331 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9334 if(this.rowSelection){
9335 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9338 findRowIndex : function(el)
9340 var cell = Roo.get(el);
9344 var row = cell.findParent('tr', false, true);
9346 if(!row || typeof(row) == 'undefined'){
9349 return this.getRowIndex(row);
9351 sort : function(e,el)
9353 var col = Roo.get(el);
9355 if(!col.hasClass('sortable')){
9359 var sort = col.attr('sort');
9362 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9366 this.store.sortInfo = {field : sort, direction : dir};
9369 Roo.log("calling footer first");
9370 this.footer.onClick('first');
9373 this.store.load({ params : { start : 0 } });
9377 renderHeader : function()
9385 this.totalWidth = 0;
9387 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9389 var config = cm.config[i];
9393 cls : 'x-hcol-' + i,
9396 html: cm.getColumnHeader(i)
9399 var tooltip = cm.getColumnTooltip(i);
9401 c.tooltip = tooltip;
9407 if(typeof(config.sortable) != 'undefined' && config.sortable){
9408 c.cls += ' sortable';
9409 c.html = '<i class="fa"></i>' + c.html;
9412 // could use BS4 hidden-..-down
9414 if(typeof(config.lgHeader) != 'undefined'){
9415 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9418 if(typeof(config.mdHeader) != 'undefined'){
9419 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9422 if(typeof(config.smHeader) != 'undefined'){
9423 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9426 if(typeof(config.xsHeader) != 'undefined'){
9427 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9434 if(typeof(config.tooltip) != 'undefined'){
9435 c.tooltip = config.tooltip;
9438 if(typeof(config.colspan) != 'undefined'){
9439 c.colspan = config.colspan;
9442 // hidden is handled by CSS now
9444 if(typeof(config.dataIndex) != 'undefined'){
9445 c.sort = config.dataIndex;
9450 if(typeof(config.align) != 'undefined' && config.align.length){
9451 c.style += ' text-align:' + config.align + ';';
9454 /* width is done in CSS
9455 *if(typeof(config.width) != 'undefined'){
9456 c.style += ' width:' + config.width + 'px;';
9457 this.totalWidth += config.width;
9459 this.totalWidth += 100; // assume minimum of 100 per column?
9463 if(typeof(config.cls) != 'undefined'){
9464 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9466 // this is the bit that doesnt reall work at all...
9468 if (this.responsive) {
9471 ['xs','sm','md','lg'].map(function(size){
9473 if(typeof(config[size]) == 'undefined'){
9477 if (!config[size]) { // 0 = hidden
9478 // BS 4 '0' is treated as hide that column and below.
9479 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9483 c.cls += ' col-' + size + '-' + config[size] + (
9484 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9492 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9503 renderBody : function()
9513 colspan : this.cm.getColumnCount()
9523 renderFooter : function()
9533 colspan : this.cm.getColumnCount()
9547 // Roo.log('ds onload');
9552 var ds = this.store;
9554 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9555 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9556 if (_this.store.sortInfo) {
9558 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9559 e.select('i', true).addClass(['fa-arrow-up']);
9562 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9563 e.select('i', true).addClass(['fa-arrow-down']);
9568 var tbody = this.bodyEl;
9570 if(ds.getCount() > 0){
9571 ds.data.each(function(d,rowIndex){
9572 var row = this.renderRow(cm, ds, rowIndex);
9574 tbody.createChild(row);
9578 if(row.cellObjects.length){
9579 Roo.each(row.cellObjects, function(r){
9580 _this.renderCellObject(r);
9587 var tfoot = this.el.select('tfoot', true).first();
9589 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9591 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9593 var total = this.ds.getTotalCount();
9595 if(this.footer.pageSize < total){
9596 this.mainFoot.show();
9600 Roo.each(this.el.select('tbody td', true).elements, function(e){
9601 e.on('mouseover', _this.onMouseover, _this);
9604 Roo.each(this.el.select('tbody td', true).elements, function(e){
9605 e.on('mouseout', _this.onMouseout, _this);
9607 this.fireEvent('rowsrendered', this);
9611 this.initCSS(); /// resize cols
9617 onUpdate : function(ds,record)
9619 this.refreshRow(record);
9623 onRemove : function(ds, record, index, isUpdate){
9624 if(isUpdate !== true){
9625 this.fireEvent("beforerowremoved", this, index, record);
9627 var bt = this.bodyEl.dom;
9629 var rows = this.el.select('tbody > tr', true).elements;
9631 if(typeof(rows[index]) != 'undefined'){
9632 bt.removeChild(rows[index].dom);
9635 // if(bt.rows[index]){
9636 // bt.removeChild(bt.rows[index]);
9639 if(isUpdate !== true){
9640 //this.stripeRows(index);
9641 //this.syncRowHeights(index, index);
9643 this.fireEvent("rowremoved", this, index, record);
9647 onAdd : function(ds, records, rowIndex)
9649 //Roo.log('on Add called');
9650 // - note this does not handle multiple adding very well..
9651 var bt = this.bodyEl.dom;
9652 for (var i =0 ; i < records.length;i++) {
9653 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9654 //Roo.log(records[i]);
9655 //Roo.log(this.store.getAt(rowIndex+i));
9656 this.insertRow(this.store, rowIndex + i, false);
9663 refreshRow : function(record){
9664 var ds = this.store, index;
9665 if(typeof record == 'number'){
9667 record = ds.getAt(index);
9669 index = ds.indexOf(record);
9671 return; // should not happen - but seems to
9674 this.insertRow(ds, index, true);
9676 this.onRemove(ds, record, index+1, true);
9678 //this.syncRowHeights(index, index);
9680 this.fireEvent("rowupdated", this, index, record);
9682 // private - called by RowSelection
9683 onRowSelect : function(rowIndex){
9684 var row = this.getRowDom(rowIndex);
9685 row.addClass(['bg-info','info']);
9687 // private - called by RowSelection
9688 onRowDeselect : function(rowIndex)
9693 var row = this.getRowDom(rowIndex);
9694 row.removeClass(['bg-info','info']);
9697 * Focuses the specified row.
9698 * @param {Number} row The row index
9700 focusRow : function(row)
9702 //Roo.log('GridView.focusRow');
9703 var x = this.bodyEl.dom.scrollLeft;
9704 this.focusCell(row, 0, false);
9705 this.bodyEl.dom.scrollLeft = x;
9709 * Focuses the specified cell.
9710 * @param {Number} row The row index
9711 * @param {Number} col The column index
9712 * @param {Boolean} hscroll false to disable horizontal scrolling
9714 focusCell : function(row, col, hscroll)
9716 //Roo.log('GridView.focusCell');
9717 var el = this.ensureVisible(row, col, hscroll);
9718 // not sure what focusEL achives = it's a <a> pos relative
9719 //this.focusEl.alignTo(el, "tl-tl");
9721 // this.focusEl.focus();
9723 // this.focusEl.focus.defer(1, this.focusEl);
9728 * Scrolls the specified cell into view
9729 * @param {Number} row The row index
9730 * @param {Number} col The column index
9731 * @param {Boolean} hscroll false to disable horizontal scrolling
9733 ensureVisible : function(row, col, hscroll)
9735 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9736 //return null; //disable for testing.
9737 if(typeof row != "number"){
9740 if(row < 0 && row >= this.ds.getCount()){
9743 col = (col !== undefined ? col : 0);
9745 while(cm.isHidden(col)){
9749 var el = this.getCellDom(row, col);
9753 var c = this.bodyEl.dom;
9755 var ctop = parseInt(el.offsetTop, 10);
9756 var cleft = parseInt(el.offsetLeft, 10);
9757 var cbot = ctop + el.offsetHeight;
9758 var cright = cleft + el.offsetWidth;
9760 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9761 var ch = 0; //?? header is not withing the area?
9762 var stop = parseInt(c.scrollTop, 10);
9763 var sleft = parseInt(c.scrollLeft, 10);
9764 var sbot = stop + ch;
9765 var sright = sleft + c.clientWidth;
9767 Roo.log('GridView.ensureVisible:' +
9769 ' c.clientHeight:' + c.clientHeight +
9770 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9779 //Roo.log("set scrolltop to ctop DISABLE?");
9780 }else if(cbot > sbot){
9781 //Roo.log("set scrolltop to cbot-ch");
9782 c.scrollTop = cbot-ch;
9785 if(hscroll !== false){
9787 c.scrollLeft = cleft;
9788 }else if(cright > sright){
9789 c.scrollLeft = cright-c.clientWidth;
9797 insertRow : function(dm, rowIndex, isUpdate){
9800 this.fireEvent("beforerowsinserted", this, rowIndex);
9802 //var s = this.getScrollState();
9803 var row = this.renderRow(this.cm, this.store, rowIndex);
9804 // insert before rowIndex..
9805 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9809 if(row.cellObjects.length){
9810 Roo.each(row.cellObjects, function(r){
9811 _this.renderCellObject(r);
9816 this.fireEvent("rowsinserted", this, rowIndex);
9817 //this.syncRowHeights(firstRow, lastRow);
9818 //this.stripeRows(firstRow);
9825 getRowDom : function(rowIndex)
9827 var rows = this.el.select('tbody > tr', true).elements;
9829 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9832 getCellDom : function(rowIndex, colIndex)
9834 var row = this.getRowDom(rowIndex);
9835 if (row === false) {
9838 var cols = row.select('td', true).elements;
9839 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9843 // returns the object tree for a tr..
9846 renderRow : function(cm, ds, rowIndex)
9848 var d = ds.getAt(rowIndex);
9852 cls : 'x-row-' + rowIndex,
9856 var cellObjects = [];
9858 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9859 var config = cm.config[i];
9861 var renderer = cm.getRenderer(i);
9865 if(typeof(renderer) !== 'undefined'){
9866 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9868 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9869 // and are rendered into the cells after the row is rendered - using the id for the element.
9871 if(typeof(value) === 'object'){
9881 rowIndex : rowIndex,
9886 this.fireEvent('rowclass', this, rowcfg);
9890 // this might end up displaying HTML?
9891 // this is too messy... - better to only do it on columsn you know are going to be too long
9892 //tooltip : (typeof(value) === 'object') ? '' : value,
9893 cls : rowcfg.rowClass + ' x-col-' + i,
9895 html: (typeof(value) === 'object') ? '' : value
9902 if(typeof(config.colspan) != 'undefined'){
9903 td.colspan = config.colspan;
9908 if(typeof(config.align) != 'undefined' && config.align.length){
9909 td.style += ' text-align:' + config.align + ';';
9911 if(typeof(config.valign) != 'undefined' && config.valign.length){
9912 td.style += ' vertical-align:' + config.valign + ';';
9915 if(typeof(config.width) != 'undefined'){
9916 td.style += ' width:' + config.width + 'px;';
9920 if(typeof(config.cursor) != 'undefined'){
9921 td.style += ' cursor:' + config.cursor + ';';
9924 if(typeof(config.cls) != 'undefined'){
9925 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9927 if (this.responsive) {
9928 ['xs','sm','md','lg'].map(function(size){
9930 if(typeof(config[size]) == 'undefined'){
9936 if (!config[size]) { // 0 = hidden
9937 // BS 4 '0' is treated as hide that column and below.
9938 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9942 td.cls += ' col-' + size + '-' + config[size] + (
9943 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9953 row.cellObjects = cellObjects;
9961 onBeforeLoad : function()
9970 this.el.select('tbody', true).first().dom.innerHTML = '';
9973 * Show or hide a row.
9974 * @param {Number} rowIndex to show or hide
9975 * @param {Boolean} state hide
9977 setRowVisibility : function(rowIndex, state)
9979 var bt = this.bodyEl.dom;
9981 var rows = this.el.select('tbody > tr', true).elements;
9983 if(typeof(rows[rowIndex]) == 'undefined'){
9986 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9991 getSelectionModel : function(){
9993 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9995 return this.selModel;
9998 * Render the Roo.bootstrap object from renderder
10000 renderCellObject : function(r)
10004 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10006 var t = r.cfg.render(r.container);
10009 Roo.each(r.cfg.cn, function(c){
10011 container: t.getChildContainer(),
10014 _this.renderCellObject(child);
10019 * get the Row Index from a dom element.
10020 * @param {Roo.Element} row The row to look for
10021 * @returns {Number} the row
10023 getRowIndex : function(row)
10027 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10038 * get the header TH element for columnIndex
10039 * @param {Number} columnIndex
10040 * @returns {Roo.Element}
10042 getHeaderIndex: function(colIndex)
10044 var cols = this.headEl.select('th', true).elements;
10045 return cols[colIndex];
10048 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10049 * @param {domElement} cell to look for
10050 * @returns {Number} the column
10052 getCellIndex : function(cell)
10054 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10056 return parseInt(id[1], 10);
10061 * Returns the grid's underlying element = used by panel.Grid
10062 * @return {Element} The element
10064 getGridEl : function(){
10068 * Forces a resize - used by panel.Grid
10069 * @return {Element} The element
10071 autoSize : function()
10073 //var ctr = Roo.get(this.container.dom.parentElement);
10074 var ctr = Roo.get(this.el.dom);
10076 var thd = this.getGridEl().select('thead',true).first();
10077 var tbd = this.getGridEl().select('tbody', true).first();
10078 var tfd = this.getGridEl().select('tfoot', true).first();
10080 var cw = ctr.getWidth();
10081 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10085 tbd.setWidth(ctr.getWidth());
10086 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10087 // this needs fixing for various usage - currently only hydra job advers I think..
10089 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10091 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10094 cw = Math.max(cw, this.totalWidth);
10095 this.getGridEl().select('tbody tr',true).setWidth(cw);
10098 // resize 'expandable coloumn?
10100 return; // we doe not have a view in this design..
10103 onBodyScroll: function()
10105 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10107 this.headEl.setStyle({
10108 'position' : 'relative',
10109 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10115 var scrollHeight = this.bodyEl.dom.scrollHeight;
10117 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10119 var height = this.bodyEl.getHeight();
10121 if(scrollHeight - height == scrollTop) {
10123 var total = this.ds.getTotalCount();
10125 if(this.footer.cursor + this.footer.pageSize < total){
10127 this.footer.ds.load({
10129 start : this.footer.cursor + this.footer.pageSize,
10130 limit : this.footer.pageSize
10139 onColumnSplitterMoved : function(i, diff)
10141 this.userResized = true;
10143 var cm = this.colModel;
10145 var w = this.getHeaderIndex(i).getWidth() + diff;
10148 cm.setColumnWidth(i, w, true);
10150 //var cid = cm.getColumnId(i); << not used in this version?
10151 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10153 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10154 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10155 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10157 //this.updateSplitters();
10158 //this.layout(); << ??
10159 this.fireEvent("columnresize", i, w);
10161 onHeaderChange : function()
10163 var header = this.renderHeader();
10164 var table = this.el.select('table', true).first();
10166 this.headEl.remove();
10167 this.headEl = table.createChild(header, this.bodyEl, false);
10169 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10170 e.on('click', this.sort, this);
10173 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10174 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10179 onHiddenChange : function(colModel, colIndex, hidden)
10182 this.cm.setHidden()
10183 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10184 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10186 this.CSS.updateRule(thSelector, "display", "");
10187 this.CSS.updateRule(tdSelector, "display", "");
10190 this.CSS.updateRule(thSelector, "display", "none");
10191 this.CSS.updateRule(tdSelector, "display", "none");
10194 // onload calls initCSS()
10195 this.onHeaderChange();
10199 setColumnWidth: function(col_index, width)
10201 // width = "md-2 xs-2..."
10202 if(!this.colModel.config[col_index]) {
10206 var w = width.split(" ");
10208 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10210 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10213 for(var j = 0; j < w.length; j++) {
10219 var size_cls = w[j].split("-");
10221 if(!Number.isInteger(size_cls[1] * 1)) {
10225 if(!this.colModel.config[col_index][size_cls[0]]) {
10229 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10233 h_row[0].classList.replace(
10234 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10235 "col-"+size_cls[0]+"-"+size_cls[1]
10238 for(var i = 0; i < rows.length; i++) {
10240 var size_cls = w[j].split("-");
10242 if(!Number.isInteger(size_cls[1] * 1)) {
10246 if(!this.colModel.config[col_index][size_cls[0]]) {
10250 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10254 rows[i].classList.replace(
10255 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10256 "col-"+size_cls[0]+"-"+size_cls[1]
10260 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10265 // currently only used to find the split on drag..
10266 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10271 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10272 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10281 * @class Roo.bootstrap.TableCell
10282 * @extends Roo.bootstrap.Component
10283 * Bootstrap TableCell class
10284 * @cfg {String} html cell contain text
10285 * @cfg {String} cls cell class
10286 * @cfg {String} tag cell tag (td|th) default td
10287 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10288 * @cfg {String} align Aligns the content in a cell
10289 * @cfg {String} axis Categorizes cells
10290 * @cfg {String} bgcolor Specifies the background color of a cell
10291 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10292 * @cfg {Number} colspan Specifies the number of columns a cell should span
10293 * @cfg {String} headers Specifies one or more header cells a cell is related to
10294 * @cfg {Number} height Sets the height of a cell
10295 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10296 * @cfg {Number} rowspan Sets the number of rows a cell should span
10297 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10298 * @cfg {String} valign Vertical aligns the content in a cell
10299 * @cfg {Number} width Specifies the width of a cell
10302 * Create a new TableCell
10303 * @param {Object} config The config object
10306 Roo.bootstrap.TableCell = function(config){
10307 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10310 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10330 getAutoCreate : function(){
10331 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10338 cfg.tag = this.tag;
10351 cfg.align=this.align
10356 if (this.bgcolor) {
10357 cfg.bgcolor=this.bgcolor
10359 if (this.charoff) {
10360 cfg.charoff=this.charoff
10362 if (this.colspan) {
10363 cfg.colspan=this.colspan
10365 if (this.headers) {
10366 cfg.headers=this.headers
10369 cfg.height=this.height
10372 cfg.nowrap=this.nowrap
10374 if (this.rowspan) {
10375 cfg.rowspan=this.rowspan
10378 cfg.scope=this.scope
10381 cfg.valign=this.valign
10384 cfg.width=this.width
10403 * @class Roo.bootstrap.TableRow
10404 * @extends Roo.bootstrap.Component
10405 * Bootstrap TableRow class
10406 * @cfg {String} cls row class
10407 * @cfg {String} align Aligns the content in a table row
10408 * @cfg {String} bgcolor Specifies a background color for a table row
10409 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10410 * @cfg {String} valign Vertical aligns the content in a table row
10413 * Create a new TableRow
10414 * @param {Object} config The config object
10417 Roo.bootstrap.TableRow = function(config){
10418 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10421 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10429 getAutoCreate : function(){
10430 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10437 cfg.cls = this.cls;
10440 cfg.align = this.align;
10443 cfg.bgcolor = this.bgcolor;
10446 cfg.charoff = this.charoff;
10449 cfg.valign = this.valign;
10467 * @class Roo.bootstrap.TableBody
10468 * @extends Roo.bootstrap.Component
10469 * Bootstrap TableBody class
10470 * @cfg {String} cls element class
10471 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10472 * @cfg {String} align Aligns the content inside the element
10473 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10474 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10477 * Create a new TableBody
10478 * @param {Object} config The config object
10481 Roo.bootstrap.TableBody = function(config){
10482 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10485 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10493 getAutoCreate : function(){
10494 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10504 cfg.tag = this.tag;
10508 cfg.align = this.align;
10511 cfg.charoff = this.charoff;
10514 cfg.valign = this.valign;
10521 // initEvents : function()
10524 // if(!this.store){
10528 // this.store = Roo.factory(this.store, Roo.data);
10529 // this.store.on('load', this.onLoad, this);
10531 // this.store.load();
10535 // onLoad: function ()
10537 // this.fireEvent('load', this);
10547 * Ext JS Library 1.1.1
10548 * Copyright(c) 2006-2007, Ext JS, LLC.
10550 * Originally Released Under LGPL - original licence link has changed is not relivant.
10553 * <script type="text/javascript">
10556 // as we use this in bootstrap.
10557 Roo.namespace('Roo.form');
10559 * @class Roo.form.Action
10560 * Internal Class used to handle form actions
10562 * @param {Roo.form.BasicForm} el The form element or its id
10563 * @param {Object} config Configuration options
10568 // define the action interface
10569 Roo.form.Action = function(form, options){
10571 this.options = options || {};
10574 * Client Validation Failed
10577 Roo.form.Action.CLIENT_INVALID = 'client';
10579 * Server Validation Failed
10582 Roo.form.Action.SERVER_INVALID = 'server';
10584 * Connect to Server Failed
10587 Roo.form.Action.CONNECT_FAILURE = 'connect';
10589 * Reading Data from Server Failed
10592 Roo.form.Action.LOAD_FAILURE = 'load';
10594 Roo.form.Action.prototype = {
10596 failureType : undefined,
10597 response : undefined,
10598 result : undefined,
10600 // interface method
10601 run : function(options){
10605 // interface method
10606 success : function(response){
10610 // interface method
10611 handleResponse : function(response){
10615 // default connection failure
10616 failure : function(response){
10618 this.response = response;
10619 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10620 this.form.afterAction(this, false);
10623 processResponse : function(response){
10624 this.response = response;
10625 if(!response.responseText){
10628 this.result = this.handleResponse(response);
10629 return this.result;
10632 // utility functions used internally
10633 getUrl : function(appendParams){
10634 var url = this.options.url || this.form.url || this.form.el.dom.action;
10636 var p = this.getParams();
10638 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10644 getMethod : function(){
10645 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10648 getParams : function(){
10649 var bp = this.form.baseParams;
10650 var p = this.options.params;
10652 if(typeof p == "object"){
10653 p = Roo.urlEncode(Roo.applyIf(p, bp));
10654 }else if(typeof p == 'string' && bp){
10655 p += '&' + Roo.urlEncode(bp);
10658 p = Roo.urlEncode(bp);
10663 createCallback : function(){
10665 success: this.success,
10666 failure: this.failure,
10668 timeout: (this.form.timeout*1000),
10669 upload: this.form.fileUpload ? this.success : undefined
10674 Roo.form.Action.Submit = function(form, options){
10675 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10678 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10681 haveProgress : false,
10682 uploadComplete : false,
10684 // uploadProgress indicator.
10685 uploadProgress : function()
10687 if (!this.form.progressUrl) {
10691 if (!this.haveProgress) {
10692 Roo.MessageBox.progress("Uploading", "Uploading");
10694 if (this.uploadComplete) {
10695 Roo.MessageBox.hide();
10699 this.haveProgress = true;
10701 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10703 var c = new Roo.data.Connection();
10705 url : this.form.progressUrl,
10710 success : function(req){
10711 //console.log(data);
10715 rdata = Roo.decode(req.responseText)
10717 Roo.log("Invalid data from server..");
10721 if (!rdata || !rdata.success) {
10723 Roo.MessageBox.alert(Roo.encode(rdata));
10726 var data = rdata.data;
10728 if (this.uploadComplete) {
10729 Roo.MessageBox.hide();
10734 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10735 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10738 this.uploadProgress.defer(2000,this);
10741 failure: function(data) {
10742 Roo.log('progress url failed ');
10753 // run get Values on the form, so it syncs any secondary forms.
10754 this.form.getValues();
10756 var o = this.options;
10757 var method = this.getMethod();
10758 var isPost = method == 'POST';
10759 if(o.clientValidation === false || this.form.isValid()){
10761 if (this.form.progressUrl) {
10762 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10763 (new Date() * 1) + '' + Math.random());
10768 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10769 form:this.form.el.dom,
10770 url:this.getUrl(!isPost),
10772 params:isPost ? this.getParams() : null,
10773 isUpload: this.form.fileUpload,
10774 formData : this.form.formData
10777 this.uploadProgress();
10779 }else if (o.clientValidation !== false){ // client validation failed
10780 this.failureType = Roo.form.Action.CLIENT_INVALID;
10781 this.form.afterAction(this, false);
10785 success : function(response)
10787 this.uploadComplete= true;
10788 if (this.haveProgress) {
10789 Roo.MessageBox.hide();
10793 var result = this.processResponse(response);
10794 if(result === true || result.success){
10795 this.form.afterAction(this, true);
10799 this.form.markInvalid(result.errors);
10800 this.failureType = Roo.form.Action.SERVER_INVALID;
10802 this.form.afterAction(this, false);
10804 failure : function(response)
10806 this.uploadComplete= true;
10807 if (this.haveProgress) {
10808 Roo.MessageBox.hide();
10811 this.response = response;
10812 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10813 this.form.afterAction(this, false);
10816 handleResponse : function(response){
10817 if(this.form.errorReader){
10818 var rs = this.form.errorReader.read(response);
10821 for(var i = 0, len = rs.records.length; i < len; i++) {
10822 var r = rs.records[i];
10823 errors[i] = r.data;
10826 if(errors.length < 1){
10830 success : rs.success,
10836 ret = Roo.decode(response.responseText);
10840 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10850 Roo.form.Action.Load = function(form, options){
10851 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10852 this.reader = this.form.reader;
10855 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10860 Roo.Ajax.request(Roo.apply(
10861 this.createCallback(), {
10862 method:this.getMethod(),
10863 url:this.getUrl(false),
10864 params:this.getParams()
10868 success : function(response){
10870 var result = this.processResponse(response);
10871 if(result === true || !result.success || !result.data){
10872 this.failureType = Roo.form.Action.LOAD_FAILURE;
10873 this.form.afterAction(this, false);
10876 this.form.clearInvalid();
10877 this.form.setValues(result.data);
10878 this.form.afterAction(this, true);
10881 handleResponse : function(response){
10882 if(this.form.reader){
10883 var rs = this.form.reader.read(response);
10884 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10886 success : rs.success,
10890 return Roo.decode(response.responseText);
10894 Roo.form.Action.ACTION_TYPES = {
10895 'load' : Roo.form.Action.Load,
10896 'submit' : Roo.form.Action.Submit
10905 * @class Roo.bootstrap.Form
10906 * @extends Roo.bootstrap.Component
10907 * Bootstrap Form class
10908 * @cfg {String} method GET | POST (default POST)
10909 * @cfg {String} labelAlign top | left (default top)
10910 * @cfg {String} align left | right - for navbars
10911 * @cfg {Boolean} loadMask load mask when submit (default true)
10915 * Create a new Form
10916 * @param {Object} config The config object
10920 Roo.bootstrap.Form = function(config){
10922 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10924 Roo.bootstrap.Form.popover.apply();
10928 * @event clientvalidation
10929 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10930 * @param {Form} this
10931 * @param {Boolean} valid true if the form has passed client-side validation
10933 clientvalidation: true,
10935 * @event beforeaction
10936 * Fires before any action is performed. Return false to cancel the action.
10937 * @param {Form} this
10938 * @param {Action} action The action to be performed
10940 beforeaction: true,
10942 * @event actionfailed
10943 * Fires when an action fails.
10944 * @param {Form} this
10945 * @param {Action} action The action that failed
10947 actionfailed : true,
10949 * @event actioncomplete
10950 * Fires when an action is completed.
10951 * @param {Form} this
10952 * @param {Action} action The action that completed
10954 actioncomplete : true
10958 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10961 * @cfg {String} method
10962 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10966 * @cfg {String} url
10967 * The URL to use for form actions if one isn't supplied in the action options.
10970 * @cfg {Boolean} fileUpload
10971 * Set to true if this form is a file upload.
10975 * @cfg {Object} baseParams
10976 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10980 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10984 * @cfg {Sting} align (left|right) for navbar forms
10989 activeAction : null,
10992 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10993 * element by passing it or its id or mask the form itself by passing in true.
10996 waitMsgTarget : false,
11001 * @cfg {Boolean} errorMask (true|false) default false
11006 * @cfg {Number} maskOffset Default 100
11011 * @cfg {Boolean} maskBody
11015 getAutoCreate : function(){
11019 method : this.method || 'POST',
11020 id : this.id || Roo.id(),
11023 if (this.parent().xtype.match(/^Nav/)) {
11024 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11028 if (this.labelAlign == 'left' ) {
11029 cfg.cls += ' form-horizontal';
11035 initEvents : function()
11037 this.el.on('submit', this.onSubmit, this);
11038 // this was added as random key presses on the form where triggering form submit.
11039 this.el.on('keypress', function(e) {
11040 if (e.getCharCode() != 13) {
11043 // we might need to allow it for textareas.. and some other items.
11044 // check e.getTarget().
11046 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11050 Roo.log("keypress blocked");
11052 e.preventDefault();
11058 onSubmit : function(e){
11063 * Returns true if client-side validation on the form is successful.
11066 isValid : function(){
11067 var items = this.getItems();
11069 var target = false;
11071 items.each(function(f){
11077 Roo.log('invalid field: ' + f.name);
11081 if(!target && f.el.isVisible(true)){
11087 if(this.errorMask && !valid){
11088 Roo.bootstrap.Form.popover.mask(this, target);
11095 * Returns true if any fields in this form have changed since their original load.
11098 isDirty : function(){
11100 var items = this.getItems();
11101 items.each(function(f){
11111 * Performs a predefined action (submit or load) or custom actions you define on this form.
11112 * @param {String} actionName The name of the action type
11113 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11114 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11115 * accept other config options):
11117 Property Type Description
11118 ---------------- --------------- ----------------------------------------------------------------------------------
11119 url String The url for the action (defaults to the form's url)
11120 method String The form method to use (defaults to the form's method, or POST if not defined)
11121 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11122 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11123 validate the form on the client (defaults to false)
11125 * @return {BasicForm} this
11127 doAction : function(action, options){
11128 if(typeof action == 'string'){
11129 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11131 if(this.fireEvent('beforeaction', this, action) !== false){
11132 this.beforeAction(action);
11133 action.run.defer(100, action);
11139 beforeAction : function(action){
11140 var o = action.options;
11145 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11147 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11150 // not really supported yet.. ??
11152 //if(this.waitMsgTarget === true){
11153 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11154 //}else if(this.waitMsgTarget){
11155 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11156 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11158 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11164 afterAction : function(action, success){
11165 this.activeAction = null;
11166 var o = action.options;
11171 Roo.get(document.body).unmask();
11177 //if(this.waitMsgTarget === true){
11178 // this.el.unmask();
11179 //}else if(this.waitMsgTarget){
11180 // this.waitMsgTarget.unmask();
11182 // Roo.MessageBox.updateProgress(1);
11183 // Roo.MessageBox.hide();
11190 Roo.callback(o.success, o.scope, [this, action]);
11191 this.fireEvent('actioncomplete', this, action);
11195 // failure condition..
11196 // we have a scenario where updates need confirming.
11197 // eg. if a locking scenario exists..
11198 // we look for { errors : { needs_confirm : true }} in the response.
11200 (typeof(action.result) != 'undefined') &&
11201 (typeof(action.result.errors) != 'undefined') &&
11202 (typeof(action.result.errors.needs_confirm) != 'undefined')
11205 Roo.log("not supported yet");
11208 Roo.MessageBox.confirm(
11209 "Change requires confirmation",
11210 action.result.errorMsg,
11215 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11225 Roo.callback(o.failure, o.scope, [this, action]);
11226 // show an error message if no failed handler is set..
11227 if (!this.hasListener('actionfailed')) {
11228 Roo.log("need to add dialog support");
11230 Roo.MessageBox.alert("Error",
11231 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11232 action.result.errorMsg :
11233 "Saving Failed, please check your entries or try again"
11238 this.fireEvent('actionfailed', this, action);
11243 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11244 * @param {String} id The value to search for
11247 findField : function(id){
11248 var items = this.getItems();
11249 var field = items.get(id);
11251 items.each(function(f){
11252 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11259 return field || null;
11262 * Mark fields in this form invalid in bulk.
11263 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11264 * @return {BasicForm} this
11266 markInvalid : function(errors){
11267 if(errors instanceof Array){
11268 for(var i = 0, len = errors.length; i < len; i++){
11269 var fieldError = errors[i];
11270 var f = this.findField(fieldError.id);
11272 f.markInvalid(fieldError.msg);
11278 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11279 field.markInvalid(errors[id]);
11283 //Roo.each(this.childForms || [], function (f) {
11284 // f.markInvalid(errors);
11291 * Set values for fields in this form in bulk.
11292 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11293 * @return {BasicForm} this
11295 setValues : function(values){
11296 if(values instanceof Array){ // array of objects
11297 for(var i = 0, len = values.length; i < len; i++){
11299 var f = this.findField(v.id);
11301 f.setValue(v.value);
11302 if(this.trackResetOnLoad){
11303 f.originalValue = f.getValue();
11307 }else{ // object hash
11310 if(typeof values[id] != 'function' && (field = this.findField(id))){
11312 if (field.setFromData &&
11313 field.valueField &&
11314 field.displayField &&
11315 // combos' with local stores can
11316 // be queried via setValue()
11317 // to set their value..
11318 (field.store && !field.store.isLocal)
11322 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11323 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11324 field.setFromData(sd);
11326 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11328 field.setFromData(values);
11331 field.setValue(values[id]);
11335 if(this.trackResetOnLoad){
11336 field.originalValue = field.getValue();
11342 //Roo.each(this.childForms || [], function (f) {
11343 // f.setValues(values);
11350 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11351 * they are returned as an array.
11352 * @param {Boolean} asString
11355 getValues : function(asString){
11356 //if (this.childForms) {
11357 // copy values from the child forms
11358 // Roo.each(this.childForms, function (f) {
11359 // this.setValues(f.getValues());
11365 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11366 if(asString === true){
11369 return Roo.urlDecode(fs);
11373 * Returns the fields in this form as an object with key/value pairs.
11374 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11377 getFieldValues : function(with_hidden)
11379 var items = this.getItems();
11381 items.each(function(f){
11383 if (!f.getName()) {
11387 var v = f.getValue();
11389 if (f.inputType =='radio') {
11390 if (typeof(ret[f.getName()]) == 'undefined') {
11391 ret[f.getName()] = ''; // empty..
11394 if (!f.el.dom.checked) {
11398 v = f.el.dom.value;
11402 if(f.xtype == 'MoneyField'){
11403 ret[f.currencyName] = f.getCurrency();
11406 // not sure if this supported any more..
11407 if ((typeof(v) == 'object') && f.getRawValue) {
11408 v = f.getRawValue() ; // dates..
11410 // combo boxes where name != hiddenName...
11411 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11412 ret[f.name] = f.getRawValue();
11414 ret[f.getName()] = v;
11421 * Clears all invalid messages in this form.
11422 * @return {BasicForm} this
11424 clearInvalid : function(){
11425 var items = this.getItems();
11427 items.each(function(f){
11435 * Resets this form.
11436 * @return {BasicForm} this
11438 reset : function(){
11439 var items = this.getItems();
11440 items.each(function(f){
11444 Roo.each(this.childForms || [], function (f) {
11452 getItems : function()
11454 var r=new Roo.util.MixedCollection(false, function(o){
11455 return o.id || (o.id = Roo.id());
11457 var iter = function(el) {
11464 Roo.each(el.items,function(e) {
11473 hideFields : function(items)
11475 Roo.each(items, function(i){
11477 var f = this.findField(i);
11488 showFields : function(items)
11490 Roo.each(items, function(i){
11492 var f = this.findField(i);
11505 Roo.apply(Roo.bootstrap.Form, {
11521 intervalID : false,
11527 if(this.isApplied){
11532 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11533 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11534 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11535 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11538 this.maskEl.top.enableDisplayMode("block");
11539 this.maskEl.left.enableDisplayMode("block");
11540 this.maskEl.bottom.enableDisplayMode("block");
11541 this.maskEl.right.enableDisplayMode("block");
11543 this.toolTip = new Roo.bootstrap.Tooltip({
11544 cls : 'roo-form-error-popover',
11546 'left' : ['r-l', [-2,0], 'right'],
11547 'right' : ['l-r', [2,0], 'left'],
11548 'bottom' : ['tl-bl', [0,2], 'top'],
11549 'top' : [ 'bl-tl', [0,-2], 'bottom']
11553 this.toolTip.render(Roo.get(document.body));
11555 this.toolTip.el.enableDisplayMode("block");
11557 Roo.get(document.body).on('click', function(){
11561 Roo.get(document.body).on('touchstart', function(){
11565 this.isApplied = true
11568 mask : function(form, target)
11572 this.target = target;
11574 if(!this.form.errorMask || !target.el){
11578 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11580 Roo.log(scrollable);
11582 var ot = this.target.el.calcOffsetsTo(scrollable);
11584 var scrollTo = ot[1] - this.form.maskOffset;
11586 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11588 scrollable.scrollTo('top', scrollTo);
11590 var box = this.target.el.getBox();
11592 var zIndex = Roo.bootstrap.Modal.zIndex++;
11595 this.maskEl.top.setStyle('position', 'absolute');
11596 this.maskEl.top.setStyle('z-index', zIndex);
11597 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11598 this.maskEl.top.setLeft(0);
11599 this.maskEl.top.setTop(0);
11600 this.maskEl.top.show();
11602 this.maskEl.left.setStyle('position', 'absolute');
11603 this.maskEl.left.setStyle('z-index', zIndex);
11604 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11605 this.maskEl.left.setLeft(0);
11606 this.maskEl.left.setTop(box.y - this.padding);
11607 this.maskEl.left.show();
11609 this.maskEl.bottom.setStyle('position', 'absolute');
11610 this.maskEl.bottom.setStyle('z-index', zIndex);
11611 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11612 this.maskEl.bottom.setLeft(0);
11613 this.maskEl.bottom.setTop(box.bottom + this.padding);
11614 this.maskEl.bottom.show();
11616 this.maskEl.right.setStyle('position', 'absolute');
11617 this.maskEl.right.setStyle('z-index', zIndex);
11618 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11619 this.maskEl.right.setLeft(box.right + this.padding);
11620 this.maskEl.right.setTop(box.y - this.padding);
11621 this.maskEl.right.show();
11623 this.toolTip.bindEl = this.target.el;
11625 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11627 var tip = this.target.blankText;
11629 if(this.target.getValue() !== '' ) {
11631 if (this.target.invalidText.length) {
11632 tip = this.target.invalidText;
11633 } else if (this.target.regexText.length){
11634 tip = this.target.regexText;
11638 this.toolTip.show(tip);
11640 this.intervalID = window.setInterval(function() {
11641 Roo.bootstrap.Form.popover.unmask();
11644 window.onwheel = function(){ return false;};
11646 (function(){ this.isMasked = true; }).defer(500, this);
11650 unmask : function()
11652 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11656 this.maskEl.top.setStyle('position', 'absolute');
11657 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11658 this.maskEl.top.hide();
11660 this.maskEl.left.setStyle('position', 'absolute');
11661 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11662 this.maskEl.left.hide();
11664 this.maskEl.bottom.setStyle('position', 'absolute');
11665 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11666 this.maskEl.bottom.hide();
11668 this.maskEl.right.setStyle('position', 'absolute');
11669 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11670 this.maskEl.right.hide();
11672 this.toolTip.hide();
11674 this.toolTip.el.hide();
11676 window.onwheel = function(){ return true;};
11678 if(this.intervalID){
11679 window.clearInterval(this.intervalID);
11680 this.intervalID = false;
11683 this.isMasked = false;
11693 * Ext JS Library 1.1.1
11694 * Copyright(c) 2006-2007, Ext JS, LLC.
11696 * Originally Released Under LGPL - original licence link has changed is not relivant.
11699 * <script type="text/javascript">
11702 * @class Roo.form.VTypes
11703 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11706 Roo.form.VTypes = function(){
11707 // closure these in so they are only created once.
11708 var alpha = /^[a-zA-Z_]+$/;
11709 var alphanum = /^[a-zA-Z0-9_]+$/;
11710 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11711 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11713 // All these messages and functions are configurable
11716 * The function used to validate email addresses
11717 * @param {String} value The email address
11719 'email' : function(v){
11720 return email.test(v);
11723 * The error text to display when the email validation function returns false
11726 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11728 * The keystroke filter mask to be applied on email input
11731 'emailMask' : /[a-z0-9_\.\-@]/i,
11734 * The function used to validate URLs
11735 * @param {String} value The URL
11737 'url' : function(v){
11738 return url.test(v);
11741 * The error text to display when the url validation function returns false
11744 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11747 * The function used to validate alpha values
11748 * @param {String} value The value
11750 'alpha' : function(v){
11751 return alpha.test(v);
11754 * The error text to display when the alpha validation function returns false
11757 'alphaText' : 'This field should only contain letters and _',
11759 * The keystroke filter mask to be applied on alpha input
11762 'alphaMask' : /[a-z_]/i,
11765 * The function used to validate alphanumeric values
11766 * @param {String} value The value
11768 'alphanum' : function(v){
11769 return alphanum.test(v);
11772 * The error text to display when the alphanumeric validation function returns false
11775 'alphanumText' : 'This field should only contain letters, numbers and _',
11777 * The keystroke filter mask to be applied on alphanumeric input
11780 'alphanumMask' : /[a-z0-9_]/i
11790 * @class Roo.bootstrap.Input
11791 * @extends Roo.bootstrap.Component
11792 * Bootstrap Input class
11793 * @cfg {Boolean} disabled is it disabled
11794 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11795 * @cfg {String} name name of the input
11796 * @cfg {string} fieldLabel - the label associated
11797 * @cfg {string} placeholder - placeholder to put in text.
11798 * @cfg {string} before - input group add on before
11799 * @cfg {string} after - input group add on after
11800 * @cfg {string} size - (lg|sm) or leave empty..
11801 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11802 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11803 * @cfg {Number} md colspan out of 12 for computer-sized screens
11804 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11805 * @cfg {string} value default value of the input
11806 * @cfg {Number} labelWidth set the width of label
11807 * @cfg {Number} labellg set the width of label (1-12)
11808 * @cfg {Number} labelmd set the width of label (1-12)
11809 * @cfg {Number} labelsm set the width of label (1-12)
11810 * @cfg {Number} labelxs set the width of label (1-12)
11811 * @cfg {String} labelAlign (top|left)
11812 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11813 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11814 * @cfg {String} indicatorpos (left|right) default left
11815 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11816 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11817 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11819 * @cfg {String} align (left|center|right) Default left
11820 * @cfg {Boolean} forceFeedback (true|false) Default false
11823 * Create a new Input
11824 * @param {Object} config The config object
11827 Roo.bootstrap.Input = function(config){
11829 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11834 * Fires when this field receives input focus.
11835 * @param {Roo.form.Field} this
11840 * Fires when this field loses input focus.
11841 * @param {Roo.form.Field} this
11845 * @event specialkey
11846 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11847 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11848 * @param {Roo.form.Field} this
11849 * @param {Roo.EventObject} e The event object
11854 * Fires just before the field blurs if the field value has changed.
11855 * @param {Roo.form.Field} this
11856 * @param {Mixed} newValue The new value
11857 * @param {Mixed} oldValue The original value
11862 * Fires after the field has been marked as invalid.
11863 * @param {Roo.form.Field} this
11864 * @param {String} msg The validation message
11869 * Fires after the field has been validated with no errors.
11870 * @param {Roo.form.Field} this
11875 * Fires after the key up
11876 * @param {Roo.form.Field} this
11877 * @param {Roo.EventObject} e The event Object
11882 * Fires after the user pastes into input
11883 * @param {Roo.form.Field} this
11884 * @param {Roo.EventObject} e The event Object
11890 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11892 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11893 automatic validation (defaults to "keyup").
11895 validationEvent : "keyup",
11897 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11899 validateOnBlur : true,
11901 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11903 validationDelay : 250,
11905 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11907 focusClass : "x-form-focus", // not needed???
11911 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11913 invalidClass : "has-warning",
11916 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11918 validClass : "has-success",
11921 * @cfg {Boolean} hasFeedback (true|false) default true
11923 hasFeedback : true,
11926 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11928 invalidFeedbackClass : "glyphicon-warning-sign",
11931 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11933 validFeedbackClass : "glyphicon-ok",
11936 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11938 selectOnFocus : false,
11941 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11945 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11950 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11952 disableKeyFilter : false,
11955 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11959 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11963 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11965 blankText : "Please complete this mandatory field",
11968 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11972 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11974 maxLength : Number.MAX_VALUE,
11976 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11978 minLengthText : "The minimum length for this field is {0}",
11980 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11982 maxLengthText : "The maximum length for this field is {0}",
11986 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11987 * If available, this function will be called only after the basic validators all return true, and will be passed the
11988 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11992 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11993 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11994 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11998 * @cfg {String} regexText -- Depricated - use Invalid Text
12003 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12009 autocomplete: false,
12013 inputType : 'text',
12016 placeholder: false,
12021 preventMark: false,
12022 isFormField : true,
12025 labelAlign : false,
12028 formatedValue : false,
12029 forceFeedback : false,
12031 indicatorpos : 'left',
12041 parentLabelAlign : function()
12044 while (parent.parent()) {
12045 parent = parent.parent();
12046 if (typeof(parent.labelAlign) !='undefined') {
12047 return parent.labelAlign;
12054 getAutoCreate : function()
12056 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12062 if(this.inputType != 'hidden'){
12063 cfg.cls = 'form-group' //input-group
12069 type : this.inputType,
12070 value : this.value,
12071 cls : 'form-control',
12072 placeholder : this.placeholder || '',
12073 autocomplete : this.autocomplete || 'new-password'
12075 if (this.inputType == 'file') {
12076 input.style = 'overflow:hidden'; // why not in CSS?
12079 if(this.capture.length){
12080 input.capture = this.capture;
12083 if(this.accept.length){
12084 input.accept = this.accept + "/*";
12088 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12091 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12092 input.maxLength = this.maxLength;
12095 if (this.disabled) {
12096 input.disabled=true;
12099 if (this.readOnly) {
12100 input.readonly=true;
12104 input.name = this.name;
12108 input.cls += ' input-' + this.size;
12112 ['xs','sm','md','lg'].map(function(size){
12113 if (settings[size]) {
12114 cfg.cls += ' col-' + size + '-' + settings[size];
12118 var inputblock = input;
12122 cls: 'glyphicon form-control-feedback'
12125 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12128 cls : 'has-feedback',
12136 if (this.before || this.after) {
12139 cls : 'input-group',
12143 if (this.before && typeof(this.before) == 'string') {
12145 inputblock.cn.push({
12147 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12151 if (this.before && typeof(this.before) == 'object') {
12152 this.before = Roo.factory(this.before);
12154 inputblock.cn.push({
12156 cls : 'roo-input-before input-group-prepend input-group-' +
12157 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12161 inputblock.cn.push(input);
12163 if (this.after && typeof(this.after) == 'string') {
12164 inputblock.cn.push({
12166 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12170 if (this.after && typeof(this.after) == 'object') {
12171 this.after = Roo.factory(this.after);
12173 inputblock.cn.push({
12175 cls : 'roo-input-after input-group-append input-group-' +
12176 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12180 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12181 inputblock.cls += ' has-feedback';
12182 inputblock.cn.push(feedback);
12187 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12188 tooltip : 'This field is required'
12190 if (this.allowBlank ) {
12191 indicator.style = this.allowBlank ? ' display:none' : '';
12193 if (align ==='left' && this.fieldLabel.length) {
12195 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12202 cls : 'control-label col-form-label',
12203 html : this.fieldLabel
12214 var labelCfg = cfg.cn[1];
12215 var contentCfg = cfg.cn[2];
12217 if(this.indicatorpos == 'right'){
12222 cls : 'control-label col-form-label',
12226 html : this.fieldLabel
12240 labelCfg = cfg.cn[0];
12241 contentCfg = cfg.cn[1];
12245 if(this.labelWidth > 12){
12246 labelCfg.style = "width: " + this.labelWidth + 'px';
12249 if(this.labelWidth < 13 && this.labelmd == 0){
12250 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12253 if(this.labellg > 0){
12254 labelCfg.cls += ' col-lg-' + this.labellg;
12255 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12258 if(this.labelmd > 0){
12259 labelCfg.cls += ' col-md-' + this.labelmd;
12260 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12263 if(this.labelsm > 0){
12264 labelCfg.cls += ' col-sm-' + this.labelsm;
12265 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12268 if(this.labelxs > 0){
12269 labelCfg.cls += ' col-xs-' + this.labelxs;
12270 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12274 } else if ( this.fieldLabel.length) {
12281 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12282 tooltip : 'This field is required',
12283 style : this.allowBlank ? ' display:none' : ''
12287 //cls : 'input-group-addon',
12288 html : this.fieldLabel
12296 if(this.indicatorpos == 'right'){
12301 //cls : 'input-group-addon',
12302 html : this.fieldLabel
12307 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12308 tooltip : 'This field is required',
12309 style : this.allowBlank ? ' display:none' : ''
12329 if (this.parentType === 'Navbar' && this.parent().bar) {
12330 cfg.cls += ' navbar-form';
12333 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12334 // on BS4 we do this only if not form
12335 cfg.cls += ' navbar-form';
12343 * return the real input element.
12345 inputEl: function ()
12347 return this.el.select('input.form-control',true).first();
12350 tooltipEl : function()
12352 return this.inputEl();
12355 indicatorEl : function()
12357 if (Roo.bootstrap.version == 4) {
12358 return false; // not enabled in v4 yet.
12361 var indicator = this.el.select('i.roo-required-indicator',true).first();
12371 setDisabled : function(v)
12373 var i = this.inputEl().dom;
12375 i.removeAttribute('disabled');
12379 i.setAttribute('disabled','true');
12381 initEvents : function()
12384 this.inputEl().on("keydown" , this.fireKey, this);
12385 this.inputEl().on("focus", this.onFocus, this);
12386 this.inputEl().on("blur", this.onBlur, this);
12388 this.inputEl().relayEvent('keyup', this);
12389 this.inputEl().relayEvent('paste', this);
12391 this.indicator = this.indicatorEl();
12393 if(this.indicator){
12394 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12397 // reference to original value for reset
12398 this.originalValue = this.getValue();
12399 //Roo.form.TextField.superclass.initEvents.call(this);
12400 if(this.validationEvent == 'keyup'){
12401 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12402 this.inputEl().on('keyup', this.filterValidation, this);
12404 else if(this.validationEvent !== false){
12405 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12408 if(this.selectOnFocus){
12409 this.on("focus", this.preFocus, this);
12412 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12413 this.inputEl().on("keypress", this.filterKeys, this);
12415 this.inputEl().relayEvent('keypress', this);
12418 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12419 this.el.on("click", this.autoSize, this);
12422 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12423 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12426 if (typeof(this.before) == 'object') {
12427 this.before.render(this.el.select('.roo-input-before',true).first());
12429 if (typeof(this.after) == 'object') {
12430 this.after.render(this.el.select('.roo-input-after',true).first());
12433 this.inputEl().on('change', this.onChange, this);
12436 filterValidation : function(e){
12437 if(!e.isNavKeyPress()){
12438 this.validationTask.delay(this.validationDelay);
12442 * Validates the field value
12443 * @return {Boolean} True if the value is valid, else false
12445 validate : function(){
12446 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12447 if(this.disabled || this.validateValue(this.getRawValue())){
12452 this.markInvalid();
12458 * Validates a value according to the field's validation rules and marks the field as invalid
12459 * if the validation fails
12460 * @param {Mixed} value The value to validate
12461 * @return {Boolean} True if the value is valid, else false
12463 validateValue : function(value)
12465 if(this.getVisibilityEl().hasClass('hidden')){
12469 if(value.length < 1) { // if it's blank
12470 if(this.allowBlank){
12476 if(value.length < this.minLength){
12479 if(value.length > this.maxLength){
12483 var vt = Roo.form.VTypes;
12484 if(!vt[this.vtype](value, this)){
12488 if(typeof this.validator == "function"){
12489 var msg = this.validator(value);
12493 if (typeof(msg) == 'string') {
12494 this.invalidText = msg;
12498 if(this.regex && !this.regex.test(value)){
12506 fireKey : function(e){
12507 //Roo.log('field ' + e.getKey());
12508 if(e.isNavKeyPress()){
12509 this.fireEvent("specialkey", this, e);
12512 focus : function (selectText){
12514 this.inputEl().focus();
12515 if(selectText === true){
12516 this.inputEl().dom.select();
12522 onFocus : function(){
12523 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12524 // this.el.addClass(this.focusClass);
12526 if(!this.hasFocus){
12527 this.hasFocus = true;
12528 this.startValue = this.getValue();
12529 this.fireEvent("focus", this);
12533 beforeBlur : Roo.emptyFn,
12537 onBlur : function(){
12539 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12540 //this.el.removeClass(this.focusClass);
12542 this.hasFocus = false;
12543 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12546 var v = this.getValue();
12547 if(String(v) !== String(this.startValue)){
12548 this.fireEvent('change', this, v, this.startValue);
12550 this.fireEvent("blur", this);
12553 onChange : function(e)
12555 var v = this.getValue();
12556 if(String(v) !== String(this.startValue)){
12557 this.fireEvent('change', this, v, this.startValue);
12563 * Resets the current field value to the originally loaded value and clears any validation messages
12565 reset : function(){
12566 this.setValue(this.originalValue);
12570 * Returns the name of the field
12571 * @return {Mixed} name The name field
12573 getName: function(){
12577 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12578 * @return {Mixed} value The field value
12580 getValue : function(){
12582 var v = this.inputEl().getValue();
12587 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12588 * @return {Mixed} value The field value
12590 getRawValue : function(){
12591 var v = this.inputEl().getValue();
12597 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12598 * @param {Mixed} value The value to set
12600 setRawValue : function(v){
12601 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12604 selectText : function(start, end){
12605 var v = this.getRawValue();
12607 start = start === undefined ? 0 : start;
12608 end = end === undefined ? v.length : end;
12609 var d = this.inputEl().dom;
12610 if(d.setSelectionRange){
12611 d.setSelectionRange(start, end);
12612 }else if(d.createTextRange){
12613 var range = d.createTextRange();
12614 range.moveStart("character", start);
12615 range.moveEnd("character", v.length-end);
12622 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12623 * @param {Mixed} value The value to set
12625 setValue : function(v){
12628 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12634 processValue : function(value){
12635 if(this.stripCharsRe){
12636 var newValue = value.replace(this.stripCharsRe, '');
12637 if(newValue !== value){
12638 this.setRawValue(newValue);
12645 preFocus : function(){
12647 if(this.selectOnFocus){
12648 this.inputEl().dom.select();
12651 filterKeys : function(e){
12652 var k = e.getKey();
12653 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12656 var c = e.getCharCode(), cc = String.fromCharCode(c);
12657 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12660 if(!this.maskRe.test(cc)){
12665 * Clear any invalid styles/messages for this field
12667 clearInvalid : function(){
12669 if(!this.el || this.preventMark){ // not rendered
12674 this.el.removeClass([this.invalidClass, 'is-invalid']);
12676 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12678 var feedback = this.el.select('.form-control-feedback', true).first();
12681 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12686 if(this.indicator){
12687 this.indicator.removeClass('visible');
12688 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12691 this.fireEvent('valid', this);
12695 * Mark this field as valid
12697 markValid : function()
12699 if(!this.el || this.preventMark){ // not rendered...
12703 this.el.removeClass([this.invalidClass, this.validClass]);
12704 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12706 var feedback = this.el.select('.form-control-feedback', true).first();
12709 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12712 if(this.indicator){
12713 this.indicator.removeClass('visible');
12714 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12722 if(this.allowBlank && !this.getRawValue().length){
12725 if (Roo.bootstrap.version == 3) {
12726 this.el.addClass(this.validClass);
12728 this.inputEl().addClass('is-valid');
12731 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12733 var feedback = this.el.select('.form-control-feedback', true).first();
12736 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12737 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12742 this.fireEvent('valid', this);
12746 * Mark this field as invalid
12747 * @param {String} msg The validation message
12749 markInvalid : function(msg)
12751 if(!this.el || this.preventMark){ // not rendered
12755 this.el.removeClass([this.invalidClass, this.validClass]);
12756 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12758 var feedback = this.el.select('.form-control-feedback', true).first();
12761 this.el.select('.form-control-feedback', true).first().removeClass(
12762 [this.invalidFeedbackClass, this.validFeedbackClass]);
12769 if(this.allowBlank && !this.getRawValue().length){
12773 if(this.indicator){
12774 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12775 this.indicator.addClass('visible');
12777 if (Roo.bootstrap.version == 3) {
12778 this.el.addClass(this.invalidClass);
12780 this.inputEl().addClass('is-invalid');
12785 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12787 var feedback = this.el.select('.form-control-feedback', true).first();
12790 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12792 if(this.getValue().length || this.forceFeedback){
12793 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12800 this.fireEvent('invalid', this, msg);
12803 SafariOnKeyDown : function(event)
12805 // this is a workaround for a password hang bug on chrome/ webkit.
12806 if (this.inputEl().dom.type != 'password') {
12810 var isSelectAll = false;
12812 if(this.inputEl().dom.selectionEnd > 0){
12813 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12815 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12816 event.preventDefault();
12821 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12823 event.preventDefault();
12824 // this is very hacky as keydown always get's upper case.
12826 var cc = String.fromCharCode(event.getCharCode());
12827 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12831 adjustWidth : function(tag, w){
12832 tag = tag.toLowerCase();
12833 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12834 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12835 if(tag == 'input'){
12838 if(tag == 'textarea'){
12841 }else if(Roo.isOpera){
12842 if(tag == 'input'){
12845 if(tag == 'textarea'){
12853 setFieldLabel : function(v)
12855 if(!this.rendered){
12859 if(this.indicatorEl()){
12860 var ar = this.el.select('label > span',true);
12862 if (ar.elements.length) {
12863 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12864 this.fieldLabel = v;
12868 var br = this.el.select('label',true);
12870 if(br.elements.length) {
12871 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12872 this.fieldLabel = v;
12876 Roo.log('Cannot Found any of label > span || label in input');
12880 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12881 this.fieldLabel = v;
12896 * @class Roo.bootstrap.TextArea
12897 * @extends Roo.bootstrap.Input
12898 * Bootstrap TextArea class
12899 * @cfg {Number} cols Specifies the visible width of a text area
12900 * @cfg {Number} rows Specifies the visible number of lines in a text area
12901 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12902 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12903 * @cfg {string} html text
12906 * Create a new TextArea
12907 * @param {Object} config The config object
12910 Roo.bootstrap.TextArea = function(config){
12911 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12915 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12925 getAutoCreate : function(){
12927 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12933 if(this.inputType != 'hidden'){
12934 cfg.cls = 'form-group' //input-group
12942 value : this.value || '',
12943 html: this.html || '',
12944 cls : 'form-control',
12945 placeholder : this.placeholder || ''
12949 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12950 input.maxLength = this.maxLength;
12954 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12958 input.cols = this.cols;
12961 if (this.readOnly) {
12962 input.readonly = true;
12966 input.name = this.name;
12970 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12974 ['xs','sm','md','lg'].map(function(size){
12975 if (settings[size]) {
12976 cfg.cls += ' col-' + size + '-' + settings[size];
12980 var inputblock = input;
12982 if(this.hasFeedback && !this.allowBlank){
12986 cls: 'glyphicon form-control-feedback'
12990 cls : 'has-feedback',
12999 if (this.before || this.after) {
13002 cls : 'input-group',
13006 inputblock.cn.push({
13008 cls : 'input-group-addon',
13013 inputblock.cn.push(input);
13015 if(this.hasFeedback && !this.allowBlank){
13016 inputblock.cls += ' has-feedback';
13017 inputblock.cn.push(feedback);
13021 inputblock.cn.push({
13023 cls : 'input-group-addon',
13030 if (align ==='left' && this.fieldLabel.length) {
13035 cls : 'control-label',
13036 html : this.fieldLabel
13047 if(this.labelWidth > 12){
13048 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13051 if(this.labelWidth < 13 && this.labelmd == 0){
13052 this.labelmd = this.labelWidth;
13055 if(this.labellg > 0){
13056 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13057 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13060 if(this.labelmd > 0){
13061 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13062 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13065 if(this.labelsm > 0){
13066 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13067 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13070 if(this.labelxs > 0){
13071 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13072 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13075 } else if ( this.fieldLabel.length) {
13080 //cls : 'input-group-addon',
13081 html : this.fieldLabel
13099 if (this.disabled) {
13100 input.disabled=true;
13107 * return the real textarea element.
13109 inputEl: function ()
13111 return this.el.select('textarea.form-control',true).first();
13115 * Clear any invalid styles/messages for this field
13117 clearInvalid : function()
13120 if(!this.el || this.preventMark){ // not rendered
13124 var label = this.el.select('label', true).first();
13125 var icon = this.el.select('i.fa-star', true).first();
13130 this.el.removeClass( this.validClass);
13131 this.inputEl().removeClass('is-invalid');
13133 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13135 var feedback = this.el.select('.form-control-feedback', true).first();
13138 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13143 this.fireEvent('valid', this);
13147 * Mark this field as valid
13149 markValid : function()
13151 if(!this.el || this.preventMark){ // not rendered
13155 this.el.removeClass([this.invalidClass, this.validClass]);
13156 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13158 var feedback = this.el.select('.form-control-feedback', true).first();
13161 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13164 if(this.disabled || this.allowBlank){
13168 var label = this.el.select('label', true).first();
13169 var icon = this.el.select('i.fa-star', true).first();
13174 if (Roo.bootstrap.version == 3) {
13175 this.el.addClass(this.validClass);
13177 this.inputEl().addClass('is-valid');
13181 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13183 var feedback = this.el.select('.form-control-feedback', true).first();
13186 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13187 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13192 this.fireEvent('valid', this);
13196 * Mark this field as invalid
13197 * @param {String} msg The validation message
13199 markInvalid : function(msg)
13201 if(!this.el || this.preventMark){ // not rendered
13205 this.el.removeClass([this.invalidClass, this.validClass]);
13206 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13208 var feedback = this.el.select('.form-control-feedback', true).first();
13211 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13214 if(this.disabled || this.allowBlank){
13218 var label = this.el.select('label', true).first();
13219 var icon = this.el.select('i.fa-star', true).first();
13221 if(!this.getValue().length && label && !icon){
13222 this.el.createChild({
13224 cls : 'text-danger fa fa-lg fa-star',
13225 tooltip : 'This field is required',
13226 style : 'margin-right:5px;'
13230 if (Roo.bootstrap.version == 3) {
13231 this.el.addClass(this.invalidClass);
13233 this.inputEl().addClass('is-invalid');
13236 // fixme ... this may be depricated need to test..
13237 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13239 var feedback = this.el.select('.form-control-feedback', true).first();
13242 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244 if(this.getValue().length || this.forceFeedback){
13245 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13252 this.fireEvent('invalid', this, msg);
13260 * trigger field - base class for combo..
13265 * @class Roo.bootstrap.TriggerField
13266 * @extends Roo.bootstrap.Input
13267 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13268 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13269 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13270 * for which you can provide a custom implementation. For example:
13272 var trigger = new Roo.bootstrap.TriggerField();
13273 trigger.onTriggerClick = myTriggerFn;
13274 trigger.applyTo('my-field');
13277 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13278 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13279 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13280 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13281 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13284 * Create a new TriggerField.
13285 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13286 * to the base TextField)
13288 Roo.bootstrap.TriggerField = function(config){
13289 this.mimicing = false;
13290 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13293 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13295 * @cfg {String} triggerClass A CSS class to apply to the trigger
13298 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13303 * @cfg {Boolean} removable (true|false) special filter default false
13307 /** @cfg {Boolean} grow @hide */
13308 /** @cfg {Number} growMin @hide */
13309 /** @cfg {Number} growMax @hide */
13315 autoSize: Roo.emptyFn,
13319 deferHeight : true,
13322 actionMode : 'wrap',
13327 getAutoCreate : function(){
13329 var align = this.labelAlign || this.parentLabelAlign();
13334 cls: 'form-group' //input-group
13341 type : this.inputType,
13342 cls : 'form-control',
13343 autocomplete: 'new-password',
13344 placeholder : this.placeholder || ''
13348 input.name = this.name;
13351 input.cls += ' input-' + this.size;
13354 if (this.disabled) {
13355 input.disabled=true;
13358 var inputblock = input;
13360 if(this.hasFeedback && !this.allowBlank){
13364 cls: 'glyphicon form-control-feedback'
13367 if(this.removable && !this.editable ){
13369 cls : 'has-feedback',
13375 cls : 'roo-combo-removable-btn close'
13382 cls : 'has-feedback',
13391 if(this.removable && !this.editable ){
13393 cls : 'roo-removable',
13399 cls : 'roo-combo-removable-btn close'
13406 if (this.before || this.after) {
13409 cls : 'input-group',
13413 inputblock.cn.push({
13415 cls : 'input-group-addon input-group-prepend input-group-text',
13420 inputblock.cn.push(input);
13422 if(this.hasFeedback && !this.allowBlank){
13423 inputblock.cls += ' has-feedback';
13424 inputblock.cn.push(feedback);
13428 inputblock.cn.push({
13430 cls : 'input-group-addon input-group-append input-group-text',
13439 var ibwrap = inputblock;
13444 cls: 'roo-select2-choices',
13448 cls: 'roo-select2-search-field',
13460 cls: 'roo-select2-container input-group',
13465 cls: 'form-hidden-field'
13471 if(!this.multiple && this.showToggleBtn){
13477 if (this.caret != false) {
13480 cls: 'fa fa-' + this.caret
13487 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13489 Roo.bootstrap.version == 3 ? caret : '',
13492 cls: 'combobox-clear',
13506 combobox.cls += ' roo-select2-container-multi';
13510 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13511 tooltip : 'This field is required'
13513 if (Roo.bootstrap.version == 4) {
13516 style : 'display:none'
13521 if (align ==='left' && this.fieldLabel.length) {
13523 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13530 cls : 'control-label',
13531 html : this.fieldLabel
13543 var labelCfg = cfg.cn[1];
13544 var contentCfg = cfg.cn[2];
13546 if(this.indicatorpos == 'right'){
13551 cls : 'control-label',
13555 html : this.fieldLabel
13569 labelCfg = cfg.cn[0];
13570 contentCfg = cfg.cn[1];
13573 if(this.labelWidth > 12){
13574 labelCfg.style = "width: " + this.labelWidth + 'px';
13577 if(this.labelWidth < 13 && this.labelmd == 0){
13578 this.labelmd = this.labelWidth;
13581 if(this.labellg > 0){
13582 labelCfg.cls += ' col-lg-' + this.labellg;
13583 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13586 if(this.labelmd > 0){
13587 labelCfg.cls += ' col-md-' + this.labelmd;
13588 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13591 if(this.labelsm > 0){
13592 labelCfg.cls += ' col-sm-' + this.labelsm;
13593 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13596 if(this.labelxs > 0){
13597 labelCfg.cls += ' col-xs-' + this.labelxs;
13598 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13601 } else if ( this.fieldLabel.length) {
13602 // Roo.log(" label");
13607 //cls : 'input-group-addon',
13608 html : this.fieldLabel
13616 if(this.indicatorpos == 'right'){
13624 html : this.fieldLabel
13638 // Roo.log(" no label && no align");
13645 ['xs','sm','md','lg'].map(function(size){
13646 if (settings[size]) {
13647 cfg.cls += ' col-' + size + '-' + settings[size];
13658 onResize : function(w, h){
13659 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13660 // if(typeof w == 'number'){
13661 // var x = w - this.trigger.getWidth();
13662 // this.inputEl().setWidth(this.adjustWidth('input', x));
13663 // this.trigger.setStyle('left', x+'px');
13668 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13671 getResizeEl : function(){
13672 return this.inputEl();
13676 getPositionEl : function(){
13677 return this.inputEl();
13681 alignErrorIcon : function(){
13682 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13686 initEvents : function(){
13690 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13691 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13692 if(!this.multiple && this.showToggleBtn){
13693 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13694 if(this.hideTrigger){
13695 this.trigger.setDisplayed(false);
13697 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13701 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13704 if(this.removable && !this.editable && !this.tickable){
13705 var close = this.closeTriggerEl();
13708 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13709 close.on('click', this.removeBtnClick, this, close);
13713 //this.trigger.addClassOnOver('x-form-trigger-over');
13714 //this.trigger.addClassOnClick('x-form-trigger-click');
13717 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13721 closeTriggerEl : function()
13723 var close = this.el.select('.roo-combo-removable-btn', true).first();
13724 return close ? close : false;
13727 removeBtnClick : function(e, h, el)
13729 e.preventDefault();
13731 if(this.fireEvent("remove", this) !== false){
13733 this.fireEvent("afterremove", this)
13737 createList : function()
13739 this.list = Roo.get(document.body).createChild({
13740 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13741 cls: 'typeahead typeahead-long dropdown-menu shadow',
13742 style: 'display:none'
13745 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13750 initTrigger : function(){
13755 onDestroy : function(){
13757 this.trigger.removeAllListeners();
13758 // this.trigger.remove();
13761 // this.wrap.remove();
13763 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13767 onFocus : function(){
13768 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13770 if(!this.mimicing){
13771 this.wrap.addClass('x-trigger-wrap-focus');
13772 this.mimicing = true;
13773 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13774 if(this.monitorTab){
13775 this.el.on("keydown", this.checkTab, this);
13782 checkTab : function(e){
13783 if(e.getKey() == e.TAB){
13784 this.triggerBlur();
13789 onBlur : function(){
13794 mimicBlur : function(e, t){
13796 if(!this.wrap.contains(t) && this.validateBlur()){
13797 this.triggerBlur();
13803 triggerBlur : function(){
13804 this.mimicing = false;
13805 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13806 if(this.monitorTab){
13807 this.el.un("keydown", this.checkTab, this);
13809 //this.wrap.removeClass('x-trigger-wrap-focus');
13810 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13814 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13815 validateBlur : function(e, t){
13820 onDisable : function(){
13821 this.inputEl().dom.disabled = true;
13822 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13824 // this.wrap.addClass('x-item-disabled');
13829 onEnable : function(){
13830 this.inputEl().dom.disabled = false;
13831 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13833 // this.el.removeClass('x-item-disabled');
13838 onShow : function(){
13839 var ae = this.getActionEl();
13842 ae.dom.style.display = '';
13843 ae.dom.style.visibility = 'visible';
13849 onHide : function(){
13850 var ae = this.getActionEl();
13851 ae.dom.style.display = 'none';
13855 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13856 * by an implementing function.
13858 * @param {EventObject} e
13860 onTriggerClick : Roo.emptyFn
13868 * @class Roo.bootstrap.CardUploader
13869 * @extends Roo.bootstrap.Button
13870 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13871 * @cfg {Number} errorTimeout default 3000
13872 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13873 * @cfg {Array} html The button text.
13877 * Create a new CardUploader
13878 * @param {Object} config The config object
13881 Roo.bootstrap.CardUploader = function(config){
13885 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13888 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13896 * When a image is clicked on - and needs to display a slideshow or similar..
13897 * @param {Roo.bootstrap.Card} this
13898 * @param {Object} The image information data
13904 * When a the download link is clicked
13905 * @param {Roo.bootstrap.Card} this
13906 * @param {Object} The image information data contains
13913 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13916 errorTimeout : 3000,
13920 fileCollection : false,
13923 getAutoCreate : function()
13927 cls :'form-group' ,
13932 //cls : 'input-group-addon',
13933 html : this.fieldLabel
13941 value : this.value,
13942 cls : 'd-none form-control'
13947 multiple : 'multiple',
13949 cls : 'd-none roo-card-upload-selector'
13953 cls : 'roo-card-uploader-button-container w-100 mb-2'
13956 cls : 'card-columns roo-card-uploader-container'
13966 getChildContainer : function() /// what children are added to.
13968 return this.containerEl;
13971 getButtonContainer : function() /// what children are added to.
13973 return this.el.select(".roo-card-uploader-button-container").first();
13976 initEvents : function()
13979 Roo.bootstrap.Input.prototype.initEvents.call(this);
13983 xns: Roo.bootstrap,
13986 container_method : 'getButtonContainer' ,
13987 html : this.html, // fix changable?
13990 'click' : function(btn, e) {
13999 this.urlAPI = (window.createObjectURL && window) ||
14000 (window.URL && URL.revokeObjectURL && URL) ||
14001 (window.webkitURL && webkitURL);
14006 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14008 this.selectorEl.on('change', this.onFileSelected, this);
14011 this.images.forEach(function(img) {
14014 this.images = false;
14016 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14022 onClick : function(e)
14024 e.preventDefault();
14026 this.selectorEl.dom.click();
14030 onFileSelected : function(e)
14032 e.preventDefault();
14034 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14038 Roo.each(this.selectorEl.dom.files, function(file){
14039 this.addFile(file);
14048 addFile : function(file)
14051 if(typeof(file) === 'string'){
14052 throw "Add file by name?"; // should not happen
14056 if(!file || !this.urlAPI){
14066 var url = _this.urlAPI.createObjectURL( file);
14069 id : Roo.bootstrap.CardUploader.ID--,
14070 is_uploaded : false,
14074 mimetype : file.type,
14082 * addCard - add an Attachment to the uploader
14083 * @param data - the data about the image to upload
14087 title : "Title of file",
14088 is_uploaded : false,
14089 src : "http://.....",
14090 srcfile : { the File upload object },
14091 mimetype : file.type,
14094 .. any other data...
14100 addCard : function (data)
14102 // hidden input element?
14103 // if the file is not an image...
14104 //then we need to use something other that and header_image
14109 xns : Roo.bootstrap,
14110 xtype : 'CardFooter',
14113 xns : Roo.bootstrap,
14119 xns : Roo.bootstrap,
14121 html : String.format("<small>{0}</small>", data.title),
14122 cls : 'col-10 text-left',
14127 click : function() {
14129 t.fireEvent( "download", t, data );
14135 xns : Roo.bootstrap,
14137 style: 'max-height: 28px; ',
14143 click : function() {
14144 t.removeCard(data.id)
14156 var cn = this.addxtype(
14159 xns : Roo.bootstrap,
14162 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14163 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14164 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14169 initEvents : function() {
14170 Roo.bootstrap.Card.prototype.initEvents.call(this);
14172 this.imgEl = this.el.select('.card-img-top').first();
14174 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14175 this.imgEl.set({ 'pointer' : 'cursor' });
14178 this.getCardFooter().addClass('p-1');
14185 // dont' really need ot update items.
14186 // this.items.push(cn);
14187 this.fileCollection.add(cn);
14189 if (!data.srcfile) {
14190 this.updateInput();
14195 var reader = new FileReader();
14196 reader.addEventListener("load", function() {
14197 data.srcdata = reader.result;
14200 reader.readAsDataURL(data.srcfile);
14205 removeCard : function(id)
14208 var card = this.fileCollection.get(id);
14209 card.data.is_deleted = 1;
14210 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14211 //this.fileCollection.remove(card);
14212 //this.items = this.items.filter(function(e) { return e != card });
14213 // dont' really need ot update items.
14214 card.el.dom.parentNode.removeChild(card.el.dom);
14215 this.updateInput();
14221 this.fileCollection.each(function(card) {
14222 if (card.el.dom && card.el.dom.parentNode) {
14223 card.el.dom.parentNode.removeChild(card.el.dom);
14226 this.fileCollection.clear();
14227 this.updateInput();
14230 updateInput : function()
14233 this.fileCollection.each(function(e) {
14237 this.inputEl().dom.value = JSON.stringify(data);
14247 Roo.bootstrap.CardUploader.ID = -1;/*
14249 * Ext JS Library 1.1.1
14250 * Copyright(c) 2006-2007, Ext JS, LLC.
14252 * Originally Released Under LGPL - original licence link has changed is not relivant.
14255 * <script type="text/javascript">
14260 * @class Roo.data.SortTypes
14262 * Defines the default sorting (casting?) comparison functions used when sorting data.
14264 Roo.data.SortTypes = {
14266 * Default sort that does nothing
14267 * @param {Mixed} s The value being converted
14268 * @return {Mixed} The comparison value
14270 none : function(s){
14275 * The regular expression used to strip tags
14279 stripTagsRE : /<\/?[^>]+>/gi,
14282 * Strips all HTML tags to sort on text only
14283 * @param {Mixed} s The value being converted
14284 * @return {String} The comparison value
14286 asText : function(s){
14287 return String(s).replace(this.stripTagsRE, "");
14291 * Strips all HTML tags to sort on text only - Case insensitive
14292 * @param {Mixed} s The value being converted
14293 * @return {String} The comparison value
14295 asUCText : function(s){
14296 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14300 * Case insensitive string
14301 * @param {Mixed} s The value being converted
14302 * @return {String} The comparison value
14304 asUCString : function(s) {
14305 return String(s).toUpperCase();
14310 * @param {Mixed} s The value being converted
14311 * @return {Number} The comparison value
14313 asDate : function(s) {
14317 if(s instanceof Date){
14318 return s.getTime();
14320 return Date.parse(String(s));
14325 * @param {Mixed} s The value being converted
14326 * @return {Float} The comparison value
14328 asFloat : function(s) {
14329 var val = parseFloat(String(s).replace(/,/g, ""));
14338 * @param {Mixed} s The value being converted
14339 * @return {Number} The comparison value
14341 asInt : function(s) {
14342 var val = parseInt(String(s).replace(/,/g, ""));
14350 * Ext JS Library 1.1.1
14351 * Copyright(c) 2006-2007, Ext JS, LLC.
14353 * Originally Released Under LGPL - original licence link has changed is not relivant.
14356 * <script type="text/javascript">
14360 * @class Roo.data.Record
14361 * Instances of this class encapsulate both record <em>definition</em> information, and record
14362 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14363 * to access Records cached in an {@link Roo.data.Store} object.<br>
14365 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14366 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14369 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14371 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14372 * {@link #create}. The parameters are the same.
14373 * @param {Array} data An associative Array of data values keyed by the field name.
14374 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14375 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14376 * not specified an integer id is generated.
14378 Roo.data.Record = function(data, id){
14379 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14384 * Generate a constructor for a specific record layout.
14385 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14386 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14387 * Each field definition object may contain the following properties: <ul>
14388 * <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,
14389 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14390 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14391 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14392 * is being used, then this is a string containing the javascript expression to reference the data relative to
14393 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14394 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14395 * this may be omitted.</p></li>
14396 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14397 * <ul><li>auto (Default, implies no conversion)</li>
14402 * <li>date</li></ul></p></li>
14403 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14404 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14405 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14406 * by the Reader into an object that will be stored in the Record. It is passed the
14407 * following parameters:<ul>
14408 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14410 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14412 * <br>usage:<br><pre><code>
14413 var TopicRecord = Roo.data.Record.create(
14414 {name: 'title', mapping: 'topic_title'},
14415 {name: 'author', mapping: 'username'},
14416 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14417 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14418 {name: 'lastPoster', mapping: 'user2'},
14419 {name: 'excerpt', mapping: 'post_text'}
14422 var myNewRecord = new TopicRecord({
14423 title: 'Do my job please',
14426 lastPost: new Date(),
14427 lastPoster: 'Animal',
14428 excerpt: 'No way dude!'
14430 myStore.add(myNewRecord);
14435 Roo.data.Record.create = function(o){
14436 var f = function(){
14437 f.superclass.constructor.apply(this, arguments);
14439 Roo.extend(f, Roo.data.Record);
14440 var p = f.prototype;
14441 p.fields = new Roo.util.MixedCollection(false, function(field){
14444 for(var i = 0, len = o.length; i < len; i++){
14445 p.fields.add(new Roo.data.Field(o[i]));
14447 f.getField = function(name){
14448 return p.fields.get(name);
14453 Roo.data.Record.AUTO_ID = 1000;
14454 Roo.data.Record.EDIT = 'edit';
14455 Roo.data.Record.REJECT = 'reject';
14456 Roo.data.Record.COMMIT = 'commit';
14458 Roo.data.Record.prototype = {
14460 * Readonly flag - true if this record has been modified.
14469 join : function(store){
14470 this.store = store;
14474 * Set the named field to the specified value.
14475 * @param {String} name The name of the field to set.
14476 * @param {Object} value The value to set the field to.
14478 set : function(name, value){
14479 if(this.data[name] == value){
14483 if(!this.modified){
14484 this.modified = {};
14486 if(typeof this.modified[name] == 'undefined'){
14487 this.modified[name] = this.data[name];
14489 this.data[name] = value;
14490 if(!this.editing && this.store){
14491 this.store.afterEdit(this);
14496 * Get the value of the named field.
14497 * @param {String} name The name of the field to get the value of.
14498 * @return {Object} The value of the field.
14500 get : function(name){
14501 return this.data[name];
14505 beginEdit : function(){
14506 this.editing = true;
14507 this.modified = {};
14511 cancelEdit : function(){
14512 this.editing = false;
14513 delete this.modified;
14517 endEdit : function(){
14518 this.editing = false;
14519 if(this.dirty && this.store){
14520 this.store.afterEdit(this);
14525 * Usually called by the {@link Roo.data.Store} which owns the Record.
14526 * Rejects all changes made to the Record since either creation, or the last commit operation.
14527 * Modified fields are reverted to their original values.
14529 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14530 * of reject operations.
14532 reject : function(){
14533 var m = this.modified;
14535 if(typeof m[n] != "function"){
14536 this.data[n] = m[n];
14539 this.dirty = false;
14540 delete this.modified;
14541 this.editing = false;
14543 this.store.afterReject(this);
14548 * Usually called by the {@link Roo.data.Store} which owns the Record.
14549 * Commits all changes made to the Record since either creation, or the last commit operation.
14551 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14552 * of commit operations.
14554 commit : function(){
14555 this.dirty = false;
14556 delete this.modified;
14557 this.editing = false;
14559 this.store.afterCommit(this);
14564 hasError : function(){
14565 return this.error != null;
14569 clearError : function(){
14574 * Creates a copy of this record.
14575 * @param {String} id (optional) A new record id if you don't want to use this record's id
14578 copy : function(newId) {
14579 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14583 * Ext JS Library 1.1.1
14584 * Copyright(c) 2006-2007, Ext JS, LLC.
14586 * Originally Released Under LGPL - original licence link has changed is not relivant.
14589 * <script type="text/javascript">
14595 * @class Roo.data.Store
14596 * @extends Roo.util.Observable
14597 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14598 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14600 * 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
14601 * has no knowledge of the format of the data returned by the Proxy.<br>
14603 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14604 * instances from the data object. These records are cached and made available through accessor functions.
14606 * Creates a new Store.
14607 * @param {Object} config A config object containing the objects needed for the Store to access data,
14608 * and read the data into Records.
14610 Roo.data.Store = function(config){
14611 this.data = new Roo.util.MixedCollection(false);
14612 this.data.getKey = function(o){
14615 this.baseParams = {};
14617 this.paramNames = {
14622 "multisort" : "_multisort"
14625 if(config && config.data){
14626 this.inlineData = config.data;
14627 delete config.data;
14630 Roo.apply(this, config);
14632 if(this.reader){ // reader passed
14633 this.reader = Roo.factory(this.reader, Roo.data);
14634 this.reader.xmodule = this.xmodule || false;
14635 if(!this.recordType){
14636 this.recordType = this.reader.recordType;
14638 if(this.reader.onMetaChange){
14639 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14643 if(this.recordType){
14644 this.fields = this.recordType.prototype.fields;
14646 this.modified = [];
14650 * @event datachanged
14651 * Fires when the data cache has changed, and a widget which is using this Store
14652 * as a Record cache should refresh its view.
14653 * @param {Store} this
14655 datachanged : true,
14657 * @event metachange
14658 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14659 * @param {Store} this
14660 * @param {Object} meta The JSON metadata
14665 * Fires when Records have been added to the Store
14666 * @param {Store} this
14667 * @param {Roo.data.Record[]} records The array of Records added
14668 * @param {Number} index The index at which the record(s) were added
14673 * Fires when a Record has been removed from the Store
14674 * @param {Store} this
14675 * @param {Roo.data.Record} record The Record that was removed
14676 * @param {Number} index The index at which the record was removed
14681 * Fires when a Record has been updated
14682 * @param {Store} this
14683 * @param {Roo.data.Record} record The Record that was updated
14684 * @param {String} operation The update operation being performed. Value may be one of:
14686 Roo.data.Record.EDIT
14687 Roo.data.Record.REJECT
14688 Roo.data.Record.COMMIT
14694 * Fires when the data cache has been cleared.
14695 * @param {Store} this
14699 * @event beforeload
14700 * Fires before a request is made for a new data object. If the beforeload handler returns false
14701 * the load action will be canceled.
14702 * @param {Store} this
14703 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14707 * @event beforeloadadd
14708 * Fires after a new set of Records has been loaded.
14709 * @param {Store} this
14710 * @param {Roo.data.Record[]} records The Records that were loaded
14711 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14713 beforeloadadd : true,
14716 * Fires after a new set of Records has been loaded, before they are added to the store.
14717 * @param {Store} this
14718 * @param {Roo.data.Record[]} records The Records that were loaded
14719 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14720 * @params {Object} return from reader
14724 * @event loadexception
14725 * Fires if an exception occurs in the Proxy during loading.
14726 * Called with the signature of the Proxy's "loadexception" event.
14727 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14730 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14731 * @param {Object} load options
14732 * @param {Object} jsonData from your request (normally this contains the Exception)
14734 loadexception : true
14738 this.proxy = Roo.factory(this.proxy, Roo.data);
14739 this.proxy.xmodule = this.xmodule || false;
14740 this.relayEvents(this.proxy, ["loadexception"]);
14742 this.sortToggle = {};
14743 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14745 Roo.data.Store.superclass.constructor.call(this);
14747 if(this.inlineData){
14748 this.loadData(this.inlineData);
14749 delete this.inlineData;
14753 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14755 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14756 * without a remote query - used by combo/forms at present.
14760 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14763 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14766 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
14767 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14770 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14771 * on any HTTP request
14774 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14777 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14781 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14782 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14784 remoteSort : false,
14787 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14788 * loaded or when a record is removed. (defaults to false).
14790 pruneModifiedRecords : false,
14793 lastOptions : null,
14796 * Add Records to the Store and fires the add event.
14797 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14799 add : function(records){
14800 records = [].concat(records);
14801 for(var i = 0, len = records.length; i < len; i++){
14802 records[i].join(this);
14804 var index = this.data.length;
14805 this.data.addAll(records);
14806 this.fireEvent("add", this, records, index);
14810 * Remove a Record from the Store and fires the remove event.
14811 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14813 remove : function(record){
14814 var index = this.data.indexOf(record);
14815 this.data.removeAt(index);
14817 if(this.pruneModifiedRecords){
14818 this.modified.remove(record);
14820 this.fireEvent("remove", this, record, index);
14824 * Remove all Records from the Store and fires the clear event.
14826 removeAll : function(){
14828 if(this.pruneModifiedRecords){
14829 this.modified = [];
14831 this.fireEvent("clear", this);
14835 * Inserts Records to the Store at the given index and fires the add event.
14836 * @param {Number} index The start index at which to insert the passed Records.
14837 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14839 insert : function(index, records){
14840 records = [].concat(records);
14841 for(var i = 0, len = records.length; i < len; i++){
14842 this.data.insert(index, records[i]);
14843 records[i].join(this);
14845 this.fireEvent("add", this, records, index);
14849 * Get the index within the cache of the passed Record.
14850 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14851 * @return {Number} The index of the passed Record. Returns -1 if not found.
14853 indexOf : function(record){
14854 return this.data.indexOf(record);
14858 * Get the index within the cache of the Record with the passed id.
14859 * @param {String} id The id of the Record to find.
14860 * @return {Number} The index of the Record. Returns -1 if not found.
14862 indexOfId : function(id){
14863 return this.data.indexOfKey(id);
14867 * Get the Record with the specified id.
14868 * @param {String} id The id of the Record to find.
14869 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14871 getById : function(id){
14872 return this.data.key(id);
14876 * Get the Record at the specified index.
14877 * @param {Number} index The index of the Record to find.
14878 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14880 getAt : function(index){
14881 return this.data.itemAt(index);
14885 * Returns a range of Records between specified indices.
14886 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14887 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14888 * @return {Roo.data.Record[]} An array of Records
14890 getRange : function(start, end){
14891 return this.data.getRange(start, end);
14895 storeOptions : function(o){
14896 o = Roo.apply({}, o);
14899 this.lastOptions = o;
14903 * Loads the Record cache from the configured Proxy using the configured Reader.
14905 * If using remote paging, then the first load call must specify the <em>start</em>
14906 * and <em>limit</em> properties in the options.params property to establish the initial
14907 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14909 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14910 * and this call will return before the new data has been loaded. Perform any post-processing
14911 * in a callback function, or in a "load" event handler.</strong>
14913 * @param {Object} options An object containing properties which control loading options:<ul>
14914 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14915 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14916 * passed the following arguments:<ul>
14917 * <li>r : Roo.data.Record[]</li>
14918 * <li>options: Options object from the load call</li>
14919 * <li>success: Boolean success indicator</li></ul></li>
14920 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14921 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14924 load : function(options){
14925 options = options || {};
14926 if(this.fireEvent("beforeload", this, options) !== false){
14927 this.storeOptions(options);
14928 var p = Roo.apply(options.params || {}, this.baseParams);
14929 // if meta was not loaded from remote source.. try requesting it.
14930 if (!this.reader.metaFromRemote) {
14931 p._requestMeta = 1;
14933 if(this.sortInfo && this.remoteSort){
14934 var pn = this.paramNames;
14935 p[pn["sort"]] = this.sortInfo.field;
14936 p[pn["dir"]] = this.sortInfo.direction;
14938 if (this.multiSort) {
14939 var pn = this.paramNames;
14940 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14943 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14948 * Reloads the Record cache from the configured Proxy using the configured Reader and
14949 * the options from the last load operation performed.
14950 * @param {Object} options (optional) An object containing properties which may override the options
14951 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14952 * the most recently used options are reused).
14954 reload : function(options){
14955 this.load(Roo.applyIf(options||{}, this.lastOptions));
14959 // Called as a callback by the Reader during a load operation.
14960 loadRecords : function(o, options, success){
14961 if(!o || success === false){
14962 if(success !== false){
14963 this.fireEvent("load", this, [], options, o);
14965 if(options.callback){
14966 options.callback.call(options.scope || this, [], options, false);
14970 // if data returned failure - throw an exception.
14971 if (o.success === false) {
14972 // show a message if no listener is registered.
14973 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14974 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14976 // loadmask wil be hooked into this..
14977 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14980 var r = o.records, t = o.totalRecords || r.length;
14982 this.fireEvent("beforeloadadd", this, r, options, o);
14984 if(!options || options.add !== true){
14985 if(this.pruneModifiedRecords){
14986 this.modified = [];
14988 for(var i = 0, len = r.length; i < len; i++){
14992 this.data = this.snapshot;
14993 delete this.snapshot;
14996 this.data.addAll(r);
14997 this.totalLength = t;
14999 this.fireEvent("datachanged", this);
15001 this.totalLength = Math.max(t, this.data.length+r.length);
15005 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15007 var e = new Roo.data.Record({});
15009 e.set(this.parent.displayField, this.parent.emptyTitle);
15010 e.set(this.parent.valueField, '');
15015 this.fireEvent("load", this, r, options, o);
15016 if(options.callback){
15017 options.callback.call(options.scope || this, r, options, true);
15023 * Loads data from a passed data block. A Reader which understands the format of the data
15024 * must have been configured in the constructor.
15025 * @param {Object} data The data block from which to read the Records. The format of the data expected
15026 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15027 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15029 loadData : function(o, append){
15030 var r = this.reader.readRecords(o);
15031 this.loadRecords(r, {add: append}, true);
15035 * using 'cn' the nested child reader read the child array into it's child stores.
15036 * @param {Object} rec The record with a 'children array
15038 loadDataFromChildren : function(rec)
15040 this.loadData(this.reader.toLoadData(rec));
15045 * Gets the number of cached records.
15047 * <em>If using paging, this may not be the total size of the dataset. If the data object
15048 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15049 * the data set size</em>
15051 getCount : function(){
15052 return this.data.length || 0;
15056 * Gets the total number of records in the dataset as returned by the server.
15058 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15059 * the dataset size</em>
15061 getTotalCount : function(){
15062 return this.totalLength || 0;
15066 * Returns the sort state of the Store as an object with two properties:
15068 field {String} The name of the field by which the Records are sorted
15069 direction {String} The sort order, "ASC" or "DESC"
15072 getSortState : function(){
15073 return this.sortInfo;
15077 applySort : function(){
15078 if(this.sortInfo && !this.remoteSort){
15079 var s = this.sortInfo, f = s.field;
15080 var st = this.fields.get(f).sortType;
15081 var fn = function(r1, r2){
15082 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15083 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15085 this.data.sort(s.direction, fn);
15086 if(this.snapshot && this.snapshot != this.data){
15087 this.snapshot.sort(s.direction, fn);
15093 * Sets the default sort column and order to be used by the next load operation.
15094 * @param {String} fieldName The name of the field to sort by.
15095 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15097 setDefaultSort : function(field, dir){
15098 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15102 * Sort the Records.
15103 * If remote sorting is used, the sort is performed on the server, and the cache is
15104 * reloaded. If local sorting is used, the cache is sorted internally.
15105 * @param {String} fieldName The name of the field to sort by.
15106 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15108 sort : function(fieldName, dir){
15109 var f = this.fields.get(fieldName);
15111 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15113 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15114 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15119 this.sortToggle[f.name] = dir;
15120 this.sortInfo = {field: f.name, direction: dir};
15121 if(!this.remoteSort){
15123 this.fireEvent("datachanged", this);
15125 this.load(this.lastOptions);
15130 * Calls the specified function for each of the Records in the cache.
15131 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15132 * Returning <em>false</em> aborts and exits the iteration.
15133 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15135 each : function(fn, scope){
15136 this.data.each(fn, scope);
15140 * Gets all records modified since the last commit. Modified records are persisted across load operations
15141 * (e.g., during paging).
15142 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15144 getModifiedRecords : function(){
15145 return this.modified;
15149 createFilterFn : function(property, value, anyMatch){
15150 if(!value.exec){ // not a regex
15151 value = String(value);
15152 if(value.length == 0){
15155 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15157 return function(r){
15158 return value.test(r.data[property]);
15163 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15164 * @param {String} property A field on your records
15165 * @param {Number} start The record index to start at (defaults to 0)
15166 * @param {Number} end The last record index to include (defaults to length - 1)
15167 * @return {Number} The sum
15169 sum : function(property, start, end){
15170 var rs = this.data.items, v = 0;
15171 start = start || 0;
15172 end = (end || end === 0) ? end : rs.length-1;
15174 for(var i = start; i <= end; i++){
15175 v += (rs[i].data[property] || 0);
15181 * Filter the records by a specified property.
15182 * @param {String} field A field on your records
15183 * @param {String/RegExp} value Either a string that the field
15184 * should start with or a RegExp to test against the field
15185 * @param {Boolean} anyMatch True to match any part not just the beginning
15187 filter : function(property, value, anyMatch){
15188 var fn = this.createFilterFn(property, value, anyMatch);
15189 return fn ? this.filterBy(fn) : this.clearFilter();
15193 * Filter by a function. The specified function will be called with each
15194 * record in this data source. If the function returns true the record is included,
15195 * otherwise it is filtered.
15196 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15197 * @param {Object} scope (optional) The scope of the function (defaults to this)
15199 filterBy : function(fn, scope){
15200 this.snapshot = this.snapshot || this.data;
15201 this.data = this.queryBy(fn, scope||this);
15202 this.fireEvent("datachanged", this);
15206 * Query the records by a specified property.
15207 * @param {String} field A field on your records
15208 * @param {String/RegExp} value Either a string that the field
15209 * should start with or a RegExp to test against the field
15210 * @param {Boolean} anyMatch True to match any part not just the beginning
15211 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15213 query : function(property, value, anyMatch){
15214 var fn = this.createFilterFn(property, value, anyMatch);
15215 return fn ? this.queryBy(fn) : this.data.clone();
15219 * Query by a function. The specified function will be called with each
15220 * record in this data source. If the function returns true the record is included
15222 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15223 * @param {Object} scope (optional) The scope of the function (defaults to this)
15224 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15226 queryBy : function(fn, scope){
15227 var data = this.snapshot || this.data;
15228 return data.filterBy(fn, scope||this);
15232 * Collects unique values for a particular dataIndex from this store.
15233 * @param {String} dataIndex The property to collect
15234 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15235 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15236 * @return {Array} An array of the unique values
15238 collect : function(dataIndex, allowNull, bypassFilter){
15239 var d = (bypassFilter === true && this.snapshot) ?
15240 this.snapshot.items : this.data.items;
15241 var v, sv, r = [], l = {};
15242 for(var i = 0, len = d.length; i < len; i++){
15243 v = d[i].data[dataIndex];
15245 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15254 * Revert to a view of the Record cache with no filtering applied.
15255 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15257 clearFilter : function(suppressEvent){
15258 if(this.snapshot && this.snapshot != this.data){
15259 this.data = this.snapshot;
15260 delete this.snapshot;
15261 if(suppressEvent !== true){
15262 this.fireEvent("datachanged", this);
15268 afterEdit : function(record){
15269 if(this.modified.indexOf(record) == -1){
15270 this.modified.push(record);
15272 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15276 afterReject : function(record){
15277 this.modified.remove(record);
15278 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15282 afterCommit : function(record){
15283 this.modified.remove(record);
15284 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15288 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15289 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15291 commitChanges : function(){
15292 var m = this.modified.slice(0);
15293 this.modified = [];
15294 for(var i = 0, len = m.length; i < len; i++){
15300 * Cancel outstanding changes on all changed records.
15302 rejectChanges : function(){
15303 var m = this.modified.slice(0);
15304 this.modified = [];
15305 for(var i = 0, len = m.length; i < len; i++){
15310 onMetaChange : function(meta, rtype, o){
15311 this.recordType = rtype;
15312 this.fields = rtype.prototype.fields;
15313 delete this.snapshot;
15314 this.sortInfo = meta.sortInfo || this.sortInfo;
15315 this.modified = [];
15316 this.fireEvent('metachange', this, this.reader.meta);
15319 moveIndex : function(data, type)
15321 var index = this.indexOf(data);
15323 var newIndex = index + type;
15327 this.insert(newIndex, data);
15332 * Ext JS Library 1.1.1
15333 * Copyright(c) 2006-2007, Ext JS, LLC.
15335 * Originally Released Under LGPL - original licence link has changed is not relivant.
15338 * <script type="text/javascript">
15342 * @class Roo.data.SimpleStore
15343 * @extends Roo.data.Store
15344 * Small helper class to make creating Stores from Array data easier.
15345 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15346 * @cfg {Array} fields An array of field definition objects, or field name strings.
15347 * @cfg {Object} an existing reader (eg. copied from another store)
15348 * @cfg {Array} data The multi-dimensional array of data
15349 * @cfg {Roo.data.DataProxy} proxy [not-required]
15350 * @cfg {Roo.data.Reader} reader [not-required]
15352 * @param {Object} config
15354 Roo.data.SimpleStore = function(config)
15356 Roo.data.SimpleStore.superclass.constructor.call(this, {
15358 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15361 Roo.data.Record.create(config.fields)
15363 proxy : new Roo.data.MemoryProxy(config.data)
15367 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15369 * Ext JS Library 1.1.1
15370 * Copyright(c) 2006-2007, Ext JS, LLC.
15372 * Originally Released Under LGPL - original licence link has changed is not relivant.
15375 * <script type="text/javascript">
15380 * @extends Roo.data.Store
15381 * @class Roo.data.JsonStore
15382 * Small helper class to make creating Stores for JSON data easier. <br/>
15384 var store = new Roo.data.JsonStore({
15385 url: 'get-images.php',
15387 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15390 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15391 * JsonReader and HttpProxy (unless inline data is provided).</b>
15392 * @cfg {Array} fields An array of field definition objects, or field name strings.
15394 * @param {Object} config
15396 Roo.data.JsonStore = function(c){
15397 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15398 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15399 reader: new Roo.data.JsonReader(c, c.fields)
15402 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15404 * Ext JS Library 1.1.1
15405 * Copyright(c) 2006-2007, Ext JS, LLC.
15407 * Originally Released Under LGPL - original licence link has changed is not relivant.
15410 * <script type="text/javascript">
15414 Roo.data.Field = function(config){
15415 if(typeof config == "string"){
15416 config = {name: config};
15418 Roo.apply(this, config);
15421 this.type = "auto";
15424 var st = Roo.data.SortTypes;
15425 // named sortTypes are supported, here we look them up
15426 if(typeof this.sortType == "string"){
15427 this.sortType = st[this.sortType];
15430 // set default sortType for strings and dates
15431 if(!this.sortType){
15434 this.sortType = st.asUCString;
15437 this.sortType = st.asDate;
15440 this.sortType = st.none;
15445 var stripRe = /[\$,%]/g;
15447 // prebuilt conversion function for this field, instead of
15448 // switching every time we're reading a value
15450 var cv, dateFormat = this.dateFormat;
15455 cv = function(v){ return v; };
15458 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15462 return v !== undefined && v !== null && v !== '' ?
15463 parseInt(String(v).replace(stripRe, ""), 10) : '';
15468 return v !== undefined && v !== null && v !== '' ?
15469 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15474 cv = function(v){ return v === true || v === "true" || v == 1; };
15481 if(v instanceof Date){
15485 if(dateFormat == "timestamp"){
15486 return new Date(v*1000);
15488 return Date.parseDate(v, dateFormat);
15490 var parsed = Date.parse(v);
15491 return parsed ? new Date(parsed) : null;
15500 Roo.data.Field.prototype = {
15508 * Ext JS Library 1.1.1
15509 * Copyright(c) 2006-2007, Ext JS, LLC.
15511 * Originally Released Under LGPL - original licence link has changed is not relivant.
15514 * <script type="text/javascript">
15517 // Base class for reading structured data from a data source. This class is intended to be
15518 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15521 * @class Roo.data.DataReader
15523 * Base class for reading structured data from a data source. This class is intended to be
15524 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15527 Roo.data.DataReader = function(meta, recordType){
15531 this.recordType = recordType instanceof Array ?
15532 Roo.data.Record.create(recordType) : recordType;
15535 Roo.data.DataReader.prototype = {
15538 readerType : 'Data',
15540 * Create an empty record
15541 * @param {Object} data (optional) - overlay some values
15542 * @return {Roo.data.Record} record created.
15544 newRow : function(d) {
15546 this.recordType.prototype.fields.each(function(c) {
15548 case 'int' : da[c.name] = 0; break;
15549 case 'date' : da[c.name] = new Date(); break;
15550 case 'float' : da[c.name] = 0.0; break;
15551 case 'boolean' : da[c.name] = false; break;
15552 default : da[c.name] = ""; break;
15556 return new this.recordType(Roo.apply(da, d));
15562 * Ext JS Library 1.1.1
15563 * Copyright(c) 2006-2007, Ext JS, LLC.
15565 * Originally Released Under LGPL - original licence link has changed is not relivant.
15568 * <script type="text/javascript">
15572 * @class Roo.data.DataProxy
15573 * @extends Roo.data.Observable
15575 * This class is an abstract base class for implementations which provide retrieval of
15576 * unformatted data objects.<br>
15578 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15579 * (of the appropriate type which knows how to parse the data object) to provide a block of
15580 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15582 * Custom implementations must implement the load method as described in
15583 * {@link Roo.data.HttpProxy#load}.
15585 Roo.data.DataProxy = function(){
15588 * @event beforeload
15589 * Fires before a network request is made to retrieve a data object.
15590 * @param {Object} This DataProxy object.
15591 * @param {Object} params The params parameter to the load function.
15596 * Fires before the load method's callback is called.
15597 * @param {Object} This DataProxy object.
15598 * @param {Object} o The data object.
15599 * @param {Object} arg The callback argument object passed to the load function.
15603 * @event loadexception
15604 * Fires if an Exception occurs during data retrieval.
15605 * @param {Object} This DataProxy object.
15606 * @param {Object} o The data object.
15607 * @param {Object} arg The callback argument object passed to the load function.
15608 * @param {Object} e The Exception.
15610 loadexception : true
15612 Roo.data.DataProxy.superclass.constructor.call(this);
15615 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15618 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15622 * Ext JS Library 1.1.1
15623 * Copyright(c) 2006-2007, Ext JS, LLC.
15625 * Originally Released Under LGPL - original licence link has changed is not relivant.
15628 * <script type="text/javascript">
15631 * @class Roo.data.MemoryProxy
15632 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15633 * to the Reader when its load method is called.
15635 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15637 Roo.data.MemoryProxy = function(data){
15641 Roo.data.MemoryProxy.superclass.constructor.call(this);
15645 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15648 * Load data from the requested source (in this case an in-memory
15649 * data object passed to the constructor), read the data object into
15650 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15651 * process that block using the passed callback.
15652 * @param {Object} params This parameter is not used by the MemoryProxy class.
15653 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15654 * object into a block of Roo.data.Records.
15655 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15656 * The function must be passed <ul>
15657 * <li>The Record block object</li>
15658 * <li>The "arg" argument from the load function</li>
15659 * <li>A boolean success indicator</li>
15661 * @param {Object} scope The scope in which to call the callback
15662 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15664 load : function(params, reader, callback, scope, arg){
15665 params = params || {};
15668 result = reader.readRecords(params.data ? params.data :this.data);
15670 this.fireEvent("loadexception", this, arg, null, e);
15671 callback.call(scope, null, arg, false);
15674 callback.call(scope, result, arg, true);
15678 update : function(params, records){
15683 * Ext JS Library 1.1.1
15684 * Copyright(c) 2006-2007, Ext JS, LLC.
15686 * Originally Released Under LGPL - original licence link has changed is not relivant.
15689 * <script type="text/javascript">
15692 * @class Roo.data.HttpProxy
15693 * @extends Roo.data.DataProxy
15694 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15695 * configured to reference a certain URL.<br><br>
15697 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15698 * from which the running page was served.<br><br>
15700 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15702 * Be aware that to enable the browser to parse an XML document, the server must set
15703 * the Content-Type header in the HTTP response to "text/xml".
15705 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15706 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15707 * will be used to make the request.
15709 Roo.data.HttpProxy = function(conn){
15710 Roo.data.HttpProxy.superclass.constructor.call(this);
15711 // is conn a conn config or a real conn?
15713 this.useAjax = !conn || !conn.events;
15717 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15718 // thse are take from connection...
15721 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15724 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15725 * extra parameters to each request made by this object. (defaults to undefined)
15728 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15729 * to each request made by this object. (defaults to undefined)
15732 * @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)
15735 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15738 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15744 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15748 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15749 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15750 * a finer-grained basis than the DataProxy events.
15752 getConnection : function(){
15753 return this.useAjax ? Roo.Ajax : this.conn;
15757 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15758 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15759 * process that block using the passed callback.
15760 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15761 * for the request to the remote server.
15762 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15763 * object into a block of Roo.data.Records.
15764 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15765 * The function must be passed <ul>
15766 * <li>The Record block object</li>
15767 * <li>The "arg" argument from the load function</li>
15768 * <li>A boolean success indicator</li>
15770 * @param {Object} scope The scope in which to call the callback
15771 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15773 load : function(params, reader, callback, scope, arg){
15774 if(this.fireEvent("beforeload", this, params) !== false){
15776 params : params || {},
15778 callback : callback,
15783 callback : this.loadResponse,
15787 Roo.applyIf(o, this.conn);
15788 if(this.activeRequest){
15789 Roo.Ajax.abort(this.activeRequest);
15791 this.activeRequest = Roo.Ajax.request(o);
15793 this.conn.request(o);
15796 callback.call(scope||this, null, arg, false);
15801 loadResponse : function(o, success, response){
15802 delete this.activeRequest;
15804 this.fireEvent("loadexception", this, o, response);
15805 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15810 result = o.reader.read(response);
15812 this.fireEvent("loadexception", this, o, response, e);
15813 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15817 this.fireEvent("load", this, o, o.request.arg);
15818 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15822 update : function(dataSet){
15827 updateResponse : function(dataSet){
15832 * Ext JS Library 1.1.1
15833 * Copyright(c) 2006-2007, Ext JS, LLC.
15835 * Originally Released Under LGPL - original licence link has changed is not relivant.
15838 * <script type="text/javascript">
15842 * @class Roo.data.ScriptTagProxy
15843 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15844 * other than the originating domain of the running page.<br><br>
15846 * <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
15847 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15849 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15850 * source code that is used as the source inside a <script> tag.<br><br>
15852 * In order for the browser to process the returned data, the server must wrap the data object
15853 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15854 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15855 * depending on whether the callback name was passed:
15858 boolean scriptTag = false;
15859 String cb = request.getParameter("callback");
15862 response.setContentType("text/javascript");
15864 response.setContentType("application/x-json");
15866 Writer out = response.getWriter();
15868 out.write(cb + "(");
15870 out.print(dataBlock.toJsonString());
15877 * @param {Object} config A configuration object.
15879 Roo.data.ScriptTagProxy = function(config){
15880 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15881 Roo.apply(this, config);
15882 this.head = document.getElementsByTagName("head")[0];
15885 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15887 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15889 * @cfg {String} url The URL from which to request the data object.
15892 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15896 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15897 * the server the name of the callback function set up by the load call to process the returned data object.
15898 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15899 * javascript output which calls this named function passing the data object as its only parameter.
15901 callbackParam : "callback",
15903 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15904 * name to the request.
15909 * Load data from the configured URL, read the data object into
15910 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15911 * process that block using the passed callback.
15912 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15913 * for the request to the remote server.
15914 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15915 * object into a block of Roo.data.Records.
15916 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15917 * The function must be passed <ul>
15918 * <li>The Record block object</li>
15919 * <li>The "arg" argument from the load function</li>
15920 * <li>A boolean success indicator</li>
15922 * @param {Object} scope The scope in which to call the callback
15923 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15925 load : function(params, reader, callback, scope, arg){
15926 if(this.fireEvent("beforeload", this, params) !== false){
15928 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15930 var url = this.url;
15931 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15933 url += "&_dc=" + (new Date().getTime());
15935 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15938 cb : "stcCallback"+transId,
15939 scriptId : "stcScript"+transId,
15943 callback : callback,
15949 window[trans.cb] = function(o){
15950 conn.handleResponse(o, trans);
15953 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15955 if(this.autoAbort !== false){
15959 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15961 var script = document.createElement("script");
15962 script.setAttribute("src", url);
15963 script.setAttribute("type", "text/javascript");
15964 script.setAttribute("id", trans.scriptId);
15965 this.head.appendChild(script);
15967 this.trans = trans;
15969 callback.call(scope||this, null, arg, false);
15974 isLoading : function(){
15975 return this.trans ? true : false;
15979 * Abort the current server request.
15981 abort : function(){
15982 if(this.isLoading()){
15983 this.destroyTrans(this.trans);
15988 destroyTrans : function(trans, isLoaded){
15989 this.head.removeChild(document.getElementById(trans.scriptId));
15990 clearTimeout(trans.timeoutId);
15992 window[trans.cb] = undefined;
15994 delete window[trans.cb];
15997 // if hasn't been loaded, wait for load to remove it to prevent script error
15998 window[trans.cb] = function(){
15999 window[trans.cb] = undefined;
16001 delete window[trans.cb];
16008 handleResponse : function(o, trans){
16009 this.trans = false;
16010 this.destroyTrans(trans, true);
16013 result = trans.reader.readRecords(o);
16015 this.fireEvent("loadexception", this, o, trans.arg, e);
16016 trans.callback.call(trans.scope||window, null, trans.arg, false);
16019 this.fireEvent("load", this, o, trans.arg);
16020 trans.callback.call(trans.scope||window, result, trans.arg, true);
16024 handleFailure : function(trans){
16025 this.trans = false;
16026 this.destroyTrans(trans, false);
16027 this.fireEvent("loadexception", this, null, trans.arg);
16028 trans.callback.call(trans.scope||window, null, trans.arg, false);
16032 * Ext JS Library 1.1.1
16033 * Copyright(c) 2006-2007, Ext JS, LLC.
16035 * Originally Released Under LGPL - original licence link has changed is not relivant.
16038 * <script type="text/javascript">
16042 * @class Roo.data.JsonReader
16043 * @extends Roo.data.DataReader
16044 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16045 * based on mappings in a provided Roo.data.Record constructor.
16047 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16048 * in the reply previously.
16053 var RecordDef = Roo.data.Record.create([
16054 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16055 {name: 'occupation'} // This field will use "occupation" as the mapping.
16057 var myReader = new Roo.data.JsonReader({
16058 totalProperty: "results", // The property which contains the total dataset size (optional)
16059 root: "rows", // The property which contains an Array of row objects
16060 id: "id" // The property within each row object that provides an ID for the record (optional)
16064 * This would consume a JSON file like this:
16066 { 'results': 2, 'rows': [
16067 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16068 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16071 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16072 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16073 * paged from the remote server.
16074 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16075 * @cfg {String} root name of the property which contains the Array of row objects.
16076 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16077 * @cfg {Array} fields Array of field definition objects
16079 * Create a new JsonReader
16080 * @param {Object} meta Metadata configuration options
16081 * @param {Object} recordType Either an Array of field definition objects,
16082 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16084 Roo.data.JsonReader = function(meta, recordType){
16087 // set some defaults:
16088 Roo.applyIf(meta, {
16089 totalProperty: 'total',
16090 successProperty : 'success',
16095 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16097 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16099 readerType : 'Json',
16102 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16103 * Used by Store query builder to append _requestMeta to params.
16106 metaFromRemote : false,
16108 * This method is only used by a DataProxy which has retrieved data from a remote server.
16109 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16110 * @return {Object} data A data block which is used by an Roo.data.Store object as
16111 * a cache of Roo.data.Records.
16113 read : function(response){
16114 var json = response.responseText;
16116 var o = /* eval:var:o */ eval("("+json+")");
16118 throw {message: "JsonReader.read: Json object not found"};
16124 this.metaFromRemote = true;
16125 this.meta = o.metaData;
16126 this.recordType = Roo.data.Record.create(o.metaData.fields);
16127 this.onMetaChange(this.meta, this.recordType, o);
16129 return this.readRecords(o);
16132 // private function a store will implement
16133 onMetaChange : function(meta, recordType, o){
16140 simpleAccess: function(obj, subsc) {
16147 getJsonAccessor: function(){
16149 return function(expr) {
16151 return(re.test(expr))
16152 ? new Function("obj", "return obj." + expr)
16157 return Roo.emptyFn;
16162 * Create a data block containing Roo.data.Records from an XML document.
16163 * @param {Object} o An object which contains an Array of row objects in the property specified
16164 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16165 * which contains the total size of the dataset.
16166 * @return {Object} data A data block which is used by an Roo.data.Store object as
16167 * a cache of Roo.data.Records.
16169 readRecords : function(o){
16171 * After any data loads, the raw JSON data is available for further custom processing.
16175 var s = this.meta, Record = this.recordType,
16176 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16178 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16180 if(s.totalProperty) {
16181 this.getTotal = this.getJsonAccessor(s.totalProperty);
16183 if(s.successProperty) {
16184 this.getSuccess = this.getJsonAccessor(s.successProperty);
16186 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16188 var g = this.getJsonAccessor(s.id);
16189 this.getId = function(rec) {
16191 return (r === undefined || r === "") ? null : r;
16194 this.getId = function(){return null;};
16197 for(var jj = 0; jj < fl; jj++){
16199 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16200 this.ef[jj] = this.getJsonAccessor(map);
16204 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16205 if(s.totalProperty){
16206 var vt = parseInt(this.getTotal(o), 10);
16211 if(s.successProperty){
16212 var vs = this.getSuccess(o);
16213 if(vs === false || vs === 'false'){
16218 for(var i = 0; i < c; i++){
16221 var id = this.getId(n);
16222 for(var j = 0; j < fl; j++){
16224 var v = this.ef[j](n);
16226 Roo.log('missing convert for ' + f.name);
16230 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16232 var record = new Record(values, id);
16234 records[i] = record;
16240 totalRecords : totalRecords
16243 // used when loading children.. @see loadDataFromChildren
16244 toLoadData: function(rec)
16246 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16247 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16248 return { data : data, total : data.length };
16253 * Ext JS Library 1.1.1
16254 * Copyright(c) 2006-2007, Ext JS, LLC.
16256 * Originally Released Under LGPL - original licence link has changed is not relivant.
16259 * <script type="text/javascript">
16263 * @class Roo.data.ArrayReader
16264 * @extends Roo.data.DataReader
16265 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16266 * Each element of that Array represents a row of data fields. The
16267 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16268 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16272 var RecordDef = Roo.data.Record.create([
16273 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16274 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16276 var myReader = new Roo.data.ArrayReader({
16277 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16281 * This would consume an Array like this:
16283 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16287 * Create a new JsonReader
16288 * @param {Object} meta Metadata configuration options.
16289 * @param {Object|Array} recordType Either an Array of field definition objects
16291 * @cfg {Array} fields Array of field definition objects
16292 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16293 * as specified to {@link Roo.data.Record#create},
16294 * or an {@link Roo.data.Record} object
16297 * created using {@link Roo.data.Record#create}.
16299 Roo.data.ArrayReader = function(meta, recordType)
16301 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16304 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16307 * Create a data block containing Roo.data.Records from an XML document.
16308 * @param {Object} o An Array of row objects which represents the dataset.
16309 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16310 * a cache of Roo.data.Records.
16312 readRecords : function(o)
16314 var sid = this.meta ? this.meta.id : null;
16315 var recordType = this.recordType, fields = recordType.prototype.fields;
16318 for(var i = 0; i < root.length; i++){
16321 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16322 for(var j = 0, jlen = fields.length; j < jlen; j++){
16323 var f = fields.items[j];
16324 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16325 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16327 values[f.name] = v;
16329 var record = new recordType(values, id);
16331 records[records.length] = record;
16335 totalRecords : records.length
16338 // used when loading children.. @see loadDataFromChildren
16339 toLoadData: function(rec)
16341 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16342 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16353 * @class Roo.bootstrap.ComboBox
16354 * @extends Roo.bootstrap.TriggerField
16355 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16356 * @cfg {Boolean} append (true|false) default false
16357 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16358 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16359 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16360 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16361 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16362 * @cfg {Boolean} animate default true
16363 * @cfg {Boolean} emptyResultText only for touch device
16364 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16365 * @cfg {String} emptyTitle default ''
16366 * @cfg {Number} width fixed with? experimental
16368 * Create a new ComboBox.
16369 * @param {Object} config Configuration options
16371 Roo.bootstrap.ComboBox = function(config){
16372 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16376 * Fires when the dropdown list is expanded
16377 * @param {Roo.bootstrap.ComboBox} combo This combo box
16382 * Fires when the dropdown list is collapsed
16383 * @param {Roo.bootstrap.ComboBox} combo This combo box
16387 * @event beforeselect
16388 * Fires before a list item is selected. Return false to cancel the selection.
16389 * @param {Roo.bootstrap.ComboBox} combo This combo box
16390 * @param {Roo.data.Record} record The data record returned from the underlying store
16391 * @param {Number} index The index of the selected item in the dropdown list
16393 'beforeselect' : true,
16396 * Fires when a list item is selected
16397 * @param {Roo.bootstrap.ComboBox} combo This combo box
16398 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16399 * @param {Number} index The index of the selected item in the dropdown list
16403 * @event beforequery
16404 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16405 * The event object passed has these properties:
16406 * @param {Roo.bootstrap.ComboBox} combo This combo box
16407 * @param {String} query The query
16408 * @param {Boolean} forceAll true to force "all" query
16409 * @param {Boolean} cancel true to cancel the query
16410 * @param {Object} e The query event object
16412 'beforequery': true,
16415 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16416 * @param {Roo.bootstrap.ComboBox} combo This combo box
16421 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16422 * @param {Roo.bootstrap.ComboBox} combo This combo box
16423 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16428 * Fires when the remove value from the combobox array
16429 * @param {Roo.bootstrap.ComboBox} combo This combo box
16433 * @event afterremove
16434 * Fires when the remove value from the combobox array
16435 * @param {Roo.bootstrap.ComboBox} combo This combo box
16437 'afterremove' : true,
16439 * @event specialfilter
16440 * Fires when specialfilter
16441 * @param {Roo.bootstrap.ComboBox} combo This combo box
16443 'specialfilter' : true,
16446 * Fires when tick the element
16447 * @param {Roo.bootstrap.ComboBox} combo This combo box
16451 * @event touchviewdisplay
16452 * Fires when touch view require special display (default is using displayField)
16453 * @param {Roo.bootstrap.ComboBox} combo This combo box
16454 * @param {Object} cfg set html .
16456 'touchviewdisplay' : true
16461 this.tickItems = [];
16463 this.selectedIndex = -1;
16464 if(this.mode == 'local'){
16465 if(config.queryDelay === undefined){
16466 this.queryDelay = 10;
16468 if(config.minChars === undefined){
16474 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16477 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16478 * rendering into an Roo.Editor, defaults to false)
16481 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16482 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16485 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16488 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16489 * the dropdown list (defaults to undefined, with no header element)
16493 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16497 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16499 listWidth: undefined,
16501 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16502 * mode = 'remote' or 'text' if mode = 'local')
16504 displayField: undefined,
16507 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16508 * mode = 'remote' or 'value' if mode = 'local').
16509 * Note: use of a valueField requires the user make a selection
16510 * in order for a value to be mapped.
16512 valueField: undefined,
16514 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16519 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16520 * field's data value (defaults to the underlying DOM element's name)
16522 hiddenName: undefined,
16524 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16528 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16530 selectedClass: 'active',
16533 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16537 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16538 * anchor positions (defaults to 'tl-bl')
16540 listAlign: 'tl-bl?',
16542 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16546 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16547 * query specified by the allQuery config option (defaults to 'query')
16549 triggerAction: 'query',
16551 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16552 * (defaults to 4, does not apply if editable = false)
16556 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16557 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16561 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16562 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16566 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16567 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16571 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16572 * when editable = true (defaults to false)
16574 selectOnFocus:false,
16576 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16578 queryParam: 'query',
16580 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16581 * when mode = 'remote' (defaults to 'Loading...')
16583 loadingText: 'Loading...',
16585 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16589 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16593 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16594 * traditional select (defaults to true)
16598 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16602 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16606 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16607 * listWidth has a higher value)
16611 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16612 * allow the user to set arbitrary text into the field (defaults to false)
16614 forceSelection:false,
16616 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16617 * if typeAhead = true (defaults to 250)
16619 typeAheadDelay : 250,
16621 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16622 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16624 valueNotFoundText : undefined,
16626 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16628 blockFocus : false,
16631 * @cfg {Boolean} disableClear Disable showing of clear button.
16633 disableClear : false,
16635 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16637 alwaysQuery : false,
16640 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16645 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16647 invalidClass : "has-warning",
16650 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16652 validClass : "has-success",
16655 * @cfg {Boolean} specialFilter (true|false) special filter default false
16657 specialFilter : false,
16660 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16662 mobileTouchView : true,
16665 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16667 useNativeIOS : false,
16670 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16672 mobile_restrict_height : false,
16674 ios_options : false,
16686 btnPosition : 'right',
16687 triggerList : true,
16688 showToggleBtn : true,
16690 emptyResultText: 'Empty',
16691 triggerText : 'Select',
16695 // element that contains real text value.. (when hidden is used..)
16697 getAutoCreate : function()
16702 * Render classic select for iso
16705 if(Roo.isIOS && this.useNativeIOS){
16706 cfg = this.getAutoCreateNativeIOS();
16714 if(Roo.isTouch && this.mobileTouchView){
16715 cfg = this.getAutoCreateTouchView();
16722 if(!this.tickable){
16723 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16728 * ComboBox with tickable selections
16731 var align = this.labelAlign || this.parentLabelAlign();
16734 cls : 'form-group roo-combobox-tickable' //input-group
16737 var btn_text_select = '';
16738 var btn_text_done = '';
16739 var btn_text_cancel = '';
16741 if (this.btn_text_show) {
16742 btn_text_select = 'Select';
16743 btn_text_done = 'Done';
16744 btn_text_cancel = 'Cancel';
16749 cls : 'tickable-buttons',
16754 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16755 //html : this.triggerText
16756 html: btn_text_select
16762 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16764 html: btn_text_done
16770 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16772 html: btn_text_cancel
16778 buttons.cn.unshift({
16780 cls: 'roo-select2-search-field-input'
16786 Roo.each(buttons.cn, function(c){
16788 c.cls += ' btn-' + _this.size;
16791 if (_this.disabled) {
16798 style : 'display: contents',
16803 cls: 'form-hidden-field'
16807 cls: 'roo-select2-choices',
16811 cls: 'roo-select2-search-field',
16822 cls: 'roo-select2-container input-group roo-select2-container-multi',
16828 // cls: 'typeahead typeahead-long dropdown-menu',
16829 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16834 if(this.hasFeedback && !this.allowBlank){
16838 cls: 'glyphicon form-control-feedback'
16841 combobox.cn.push(feedback);
16848 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16849 tooltip : 'This field is required'
16851 if (Roo.bootstrap.version == 4) {
16854 style : 'display:none'
16857 if (align ==='left' && this.fieldLabel.length) {
16859 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16866 cls : 'control-label col-form-label',
16867 html : this.fieldLabel
16879 var labelCfg = cfg.cn[1];
16880 var contentCfg = cfg.cn[2];
16883 if(this.indicatorpos == 'right'){
16889 cls : 'control-label col-form-label',
16893 html : this.fieldLabel
16909 labelCfg = cfg.cn[0];
16910 contentCfg = cfg.cn[1];
16914 if(this.labelWidth > 12){
16915 labelCfg.style = "width: " + this.labelWidth + 'px';
16917 if(this.width * 1 > 0){
16918 contentCfg.style = "width: " + this.width + 'px';
16920 if(this.labelWidth < 13 && this.labelmd == 0){
16921 this.labelmd = this.labelWidth;
16924 if(this.labellg > 0){
16925 labelCfg.cls += ' col-lg-' + this.labellg;
16926 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16929 if(this.labelmd > 0){
16930 labelCfg.cls += ' col-md-' + this.labelmd;
16931 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16934 if(this.labelsm > 0){
16935 labelCfg.cls += ' col-sm-' + this.labelsm;
16936 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16939 if(this.labelxs > 0){
16940 labelCfg.cls += ' col-xs-' + this.labelxs;
16941 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16945 } else if ( this.fieldLabel.length) {
16946 // Roo.log(" label");
16951 //cls : 'input-group-addon',
16952 html : this.fieldLabel
16957 if(this.indicatorpos == 'right'){
16961 //cls : 'input-group-addon',
16962 html : this.fieldLabel
16972 // Roo.log(" no label && no align");
16979 ['xs','sm','md','lg'].map(function(size){
16980 if (settings[size]) {
16981 cfg.cls += ' col-' + size + '-' + settings[size];
16989 _initEventsCalled : false,
16992 initEvents: function()
16994 if (this._initEventsCalled) { // as we call render... prevent looping...
16997 this._initEventsCalled = true;
17000 throw "can not find store for combo";
17003 this.indicator = this.indicatorEl();
17005 this.store = Roo.factory(this.store, Roo.data);
17006 this.store.parent = this;
17008 // if we are building from html. then this element is so complex, that we can not really
17009 // use the rendered HTML.
17010 // so we have to trash and replace the previous code.
17011 if (Roo.XComponent.build_from_html) {
17012 // remove this element....
17013 var e = this.el.dom, k=0;
17014 while (e ) { e = e.previousSibling; ++k;}
17019 this.rendered = false;
17021 this.render(this.parent().getChildContainer(true), k);
17024 if(Roo.isIOS && this.useNativeIOS){
17025 this.initIOSView();
17033 if(Roo.isTouch && this.mobileTouchView){
17034 this.initTouchView();
17039 this.initTickableEvents();
17043 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17045 if(this.hiddenName){
17047 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17049 this.hiddenField.dom.value =
17050 this.hiddenValue !== undefined ? this.hiddenValue :
17051 this.value !== undefined ? this.value : '';
17053 // prevent input submission
17054 this.el.dom.removeAttribute('name');
17055 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17060 // this.el.dom.setAttribute('autocomplete', 'off');
17063 var cls = 'x-combo-list';
17065 //this.list = new Roo.Layer({
17066 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17072 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17073 _this.list.setWidth(lw);
17076 this.list.on('mouseover', this.onViewOver, this);
17077 this.list.on('mousemove', this.onViewMove, this);
17078 this.list.on('scroll', this.onViewScroll, this);
17081 this.list.swallowEvent('mousewheel');
17082 this.assetHeight = 0;
17085 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17086 this.assetHeight += this.header.getHeight();
17089 this.innerList = this.list.createChild({cls:cls+'-inner'});
17090 this.innerList.on('mouseover', this.onViewOver, this);
17091 this.innerList.on('mousemove', this.onViewMove, this);
17092 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17094 if(this.allowBlank && !this.pageSize && !this.disableClear){
17095 this.footer = this.list.createChild({cls:cls+'-ft'});
17096 this.pageTb = new Roo.Toolbar(this.footer);
17100 this.footer = this.list.createChild({cls:cls+'-ft'});
17101 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17102 {pageSize: this.pageSize});
17106 if (this.pageTb && this.allowBlank && !this.disableClear) {
17108 this.pageTb.add(new Roo.Toolbar.Fill(), {
17109 cls: 'x-btn-icon x-btn-clear',
17111 handler: function()
17114 _this.clearValue();
17115 _this.onSelect(false, -1);
17120 this.assetHeight += this.footer.getHeight();
17125 this.tpl = Roo.bootstrap.version == 4 ?
17126 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17127 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17130 this.view = new Roo.View(this.list, this.tpl, {
17131 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17133 //this.view.wrapEl.setDisplayed(false);
17134 this.view.on('click', this.onViewClick, this);
17137 this.store.on('beforeload', this.onBeforeLoad, this);
17138 this.store.on('load', this.onLoad, this);
17139 this.store.on('loadexception', this.onLoadException, this);
17141 if(this.resizable){
17142 this.resizer = new Roo.Resizable(this.list, {
17143 pinned:true, handles:'se'
17145 this.resizer.on('resize', function(r, w, h){
17146 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17147 this.listWidth = w;
17148 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17149 this.restrictHeight();
17151 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17154 if(!this.editable){
17155 this.editable = true;
17156 this.setEditable(false);
17161 if (typeof(this.events.add.listeners) != 'undefined') {
17163 this.addicon = this.wrap.createChild(
17164 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17166 this.addicon.on('click', function(e) {
17167 this.fireEvent('add', this);
17170 if (typeof(this.events.edit.listeners) != 'undefined') {
17172 this.editicon = this.wrap.createChild(
17173 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17174 if (this.addicon) {
17175 this.editicon.setStyle('margin-left', '40px');
17177 this.editicon.on('click', function(e) {
17179 // we fire even if inothing is selected..
17180 this.fireEvent('edit', this, this.lastData );
17186 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17187 "up" : function(e){
17188 this.inKeyMode = true;
17192 "down" : function(e){
17193 if(!this.isExpanded()){
17194 this.onTriggerClick();
17196 this.inKeyMode = true;
17201 "enter" : function(e){
17202 // this.onViewClick();
17206 if(this.fireEvent("specialkey", this, e)){
17207 this.onViewClick(false);
17213 "esc" : function(e){
17217 "tab" : function(e){
17220 if(this.fireEvent("specialkey", this, e)){
17221 this.onViewClick(false);
17229 doRelay : function(foo, bar, hname){
17230 if(hname == 'down' || this.scope.isExpanded()){
17231 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17240 this.queryDelay = Math.max(this.queryDelay || 10,
17241 this.mode == 'local' ? 10 : 250);
17244 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17246 if(this.typeAhead){
17247 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17249 if(this.editable !== false){
17250 this.inputEl().on("keyup", this.onKeyUp, this);
17252 if(this.forceSelection){
17253 this.inputEl().on('blur', this.doForce, this);
17257 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17258 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17262 initTickableEvents: function()
17266 if(this.hiddenName){
17268 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17270 this.hiddenField.dom.value =
17271 this.hiddenValue !== undefined ? this.hiddenValue :
17272 this.value !== undefined ? this.value : '';
17274 // prevent input submission
17275 this.el.dom.removeAttribute('name');
17276 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17281 // this.list = this.el.select('ul.dropdown-menu',true).first();
17283 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17284 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17285 if(this.triggerList){
17286 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17289 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17290 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17292 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17293 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17295 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17296 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17298 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17299 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17300 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17303 this.cancelBtn.hide();
17308 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17309 _this.list.setWidth(lw);
17312 this.list.on('mouseover', this.onViewOver, this);
17313 this.list.on('mousemove', this.onViewMove, this);
17315 this.list.on('scroll', this.onViewScroll, this);
17318 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17319 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17322 this.view = new Roo.View(this.list, this.tpl, {
17327 selectedClass: this.selectedClass
17330 //this.view.wrapEl.setDisplayed(false);
17331 this.view.on('click', this.onViewClick, this);
17335 this.store.on('beforeload', this.onBeforeLoad, this);
17336 this.store.on('load', this.onLoad, this);
17337 this.store.on('loadexception', this.onLoadException, this);
17340 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17341 "up" : function(e){
17342 this.inKeyMode = true;
17346 "down" : function(e){
17347 this.inKeyMode = true;
17351 "enter" : function(e){
17352 if(this.fireEvent("specialkey", this, e)){
17353 this.onViewClick(false);
17359 "esc" : function(e){
17360 this.onTickableFooterButtonClick(e, false, false);
17363 "tab" : function(e){
17364 this.fireEvent("specialkey", this, e);
17366 this.onTickableFooterButtonClick(e, false, false);
17373 doRelay : function(e, fn, key){
17374 if(this.scope.isExpanded()){
17375 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17384 this.queryDelay = Math.max(this.queryDelay || 10,
17385 this.mode == 'local' ? 10 : 250);
17388 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17390 if(this.typeAhead){
17391 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17394 if(this.editable !== false){
17395 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17398 this.indicator = this.indicatorEl();
17400 if(this.indicator){
17401 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17402 this.indicator.hide();
17407 onDestroy : function(){
17409 this.view.setStore(null);
17410 this.view.el.removeAllListeners();
17411 this.view.el.remove();
17412 this.view.purgeListeners();
17415 this.list.dom.innerHTML = '';
17419 this.store.un('beforeload', this.onBeforeLoad, this);
17420 this.store.un('load', this.onLoad, this);
17421 this.store.un('loadexception', this.onLoadException, this);
17423 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17427 fireKey : function(e){
17428 if(e.isNavKeyPress() && !this.list.isVisible()){
17429 this.fireEvent("specialkey", this, e);
17434 onResize: function(w, h)
17438 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17440 // if(typeof w != 'number'){
17441 // // we do not handle it!?!?
17444 // var tw = this.trigger.getWidth();
17445 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17446 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17448 // this.inputEl().setWidth( this.adjustWidth('input', x));
17450 // //this.trigger.setStyle('left', x+'px');
17452 // if(this.list && this.listWidth === undefined){
17453 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17454 // this.list.setWidth(lw);
17455 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17463 * Allow or prevent the user from directly editing the field text. If false is passed,
17464 * the user will only be able to select from the items defined in the dropdown list. This method
17465 * is the runtime equivalent of setting the 'editable' config option at config time.
17466 * @param {Boolean} value True to allow the user to directly edit the field text
17468 setEditable : function(value){
17469 if(value == this.editable){
17472 this.editable = value;
17474 this.inputEl().dom.setAttribute('readOnly', true);
17475 this.inputEl().on('mousedown', this.onTriggerClick, this);
17476 this.inputEl().addClass('x-combo-noedit');
17478 this.inputEl().dom.removeAttribute('readOnly');
17479 this.inputEl().un('mousedown', this.onTriggerClick, this);
17480 this.inputEl().removeClass('x-combo-noedit');
17486 onBeforeLoad : function(combo,opts){
17487 if(!this.hasFocus){
17491 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17493 this.restrictHeight();
17494 this.selectedIndex = -1;
17498 onLoad : function(){
17500 this.hasQuery = false;
17502 if(!this.hasFocus){
17506 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17507 this.loading.hide();
17510 if(this.store.getCount() > 0){
17513 this.restrictHeight();
17514 if(this.lastQuery == this.allQuery){
17515 if(this.editable && !this.tickable){
17516 this.inputEl().dom.select();
17520 !this.selectByValue(this.value, true) &&
17523 !this.store.lastOptions ||
17524 typeof(this.store.lastOptions.add) == 'undefined' ||
17525 this.store.lastOptions.add != true
17528 this.select(0, true);
17531 if(this.autoFocus){
17534 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17535 this.taTask.delay(this.typeAheadDelay);
17539 this.onEmptyResults();
17545 onLoadException : function()
17547 this.hasQuery = false;
17549 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17550 this.loading.hide();
17553 if(this.tickable && this.editable){
17558 // only causes errors at present
17559 //Roo.log(this.store.reader.jsonData);
17560 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17562 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17568 onTypeAhead : function(){
17569 if(this.store.getCount() > 0){
17570 var r = this.store.getAt(0);
17571 var newValue = r.data[this.displayField];
17572 var len = newValue.length;
17573 var selStart = this.getRawValue().length;
17575 if(selStart != len){
17576 this.setRawValue(newValue);
17577 this.selectText(selStart, newValue.length);
17583 onSelect : function(record, index){
17585 if(this.fireEvent('beforeselect', this, record, index) !== false){
17587 this.setFromData(index > -1 ? record.data : false);
17590 this.fireEvent('select', this, record, index);
17595 * Returns the currently selected field value or empty string if no value is set.
17596 * @return {String} value The selected value
17598 getValue : function()
17600 if(Roo.isIOS && this.useNativeIOS){
17601 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17605 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17608 if(this.valueField){
17609 return typeof this.value != 'undefined' ? this.value : '';
17611 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17615 getRawValue : function()
17617 if(Roo.isIOS && this.useNativeIOS){
17618 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17621 var v = this.inputEl().getValue();
17627 * Clears any text/value currently set in the field
17629 clearValue : function(){
17631 if(this.hiddenField){
17632 this.hiddenField.dom.value = '';
17635 this.setRawValue('');
17636 this.lastSelectionText = '';
17637 this.lastData = false;
17639 var close = this.closeTriggerEl();
17650 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17651 * will be displayed in the field. If the value does not match the data value of an existing item,
17652 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17653 * Otherwise the field will be blank (although the value will still be set).
17654 * @param {String} value The value to match
17656 setValue : function(v)
17658 if(Roo.isIOS && this.useNativeIOS){
17659 this.setIOSValue(v);
17669 if(this.valueField){
17670 var r = this.findRecord(this.valueField, v);
17672 text = r.data[this.displayField];
17673 }else if(this.valueNotFoundText !== undefined){
17674 text = this.valueNotFoundText;
17677 this.lastSelectionText = text;
17678 if(this.hiddenField){
17679 this.hiddenField.dom.value = v;
17681 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17684 var close = this.closeTriggerEl();
17687 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17693 * @property {Object} the last set data for the element
17698 * Sets the value of the field based on a object which is related to the record format for the store.
17699 * @param {Object} value the value to set as. or false on reset?
17701 setFromData : function(o){
17708 var dv = ''; // display value
17709 var vv = ''; // value value..
17711 if (this.displayField) {
17712 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17714 // this is an error condition!!!
17715 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17718 if(this.valueField){
17719 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17722 var close = this.closeTriggerEl();
17725 if(dv.length || vv * 1 > 0){
17727 this.blockFocus=true;
17733 if(this.hiddenField){
17734 this.hiddenField.dom.value = vv;
17736 this.lastSelectionText = dv;
17737 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17741 // no hidden field.. - we store the value in 'value', but still display
17742 // display field!!!!
17743 this.lastSelectionText = dv;
17744 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17751 reset : function(){
17752 // overridden so that last data is reset..
17759 this.setValue(this.originalValue);
17760 //this.clearInvalid();
17761 this.lastData = false;
17763 this.view.clearSelections();
17769 findRecord : function(prop, value){
17771 if(this.store.getCount() > 0){
17772 this.store.each(function(r){
17773 if(r.data[prop] == value){
17783 getName: function()
17785 // returns hidden if it's set..
17786 if (!this.rendered) {return ''};
17787 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17791 onViewMove : function(e, t){
17792 this.inKeyMode = false;
17796 onViewOver : function(e, t){
17797 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17800 var item = this.view.findItemFromChild(t);
17803 var index = this.view.indexOf(item);
17804 this.select(index, false);
17809 onViewClick : function(view, doFocus, el, e)
17811 var index = this.view.getSelectedIndexes()[0];
17813 var r = this.store.getAt(index);
17817 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17824 Roo.each(this.tickItems, function(v,k){
17826 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17828 _this.tickItems.splice(k, 1);
17830 if(typeof(e) == 'undefined' && view == false){
17831 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17843 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17844 this.tickItems.push(r.data);
17847 if(typeof(e) == 'undefined' && view == false){
17848 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17855 this.onSelect(r, index);
17857 if(doFocus !== false && !this.blockFocus){
17858 this.inputEl().focus();
17863 restrictHeight : function(){
17864 //this.innerList.dom.style.height = '';
17865 //var inner = this.innerList.dom;
17866 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17867 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17868 //this.list.beginUpdate();
17869 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17870 this.list.alignTo(this.inputEl(), this.listAlign);
17871 this.list.alignTo(this.inputEl(), this.listAlign);
17872 //this.list.endUpdate();
17876 onEmptyResults : function(){
17878 if(this.tickable && this.editable){
17879 this.hasFocus = false;
17880 this.restrictHeight();
17888 * Returns true if the dropdown list is expanded, else false.
17890 isExpanded : function(){
17891 return this.list.isVisible();
17895 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17896 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17897 * @param {String} value The data value of the item to select
17898 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17899 * selected item if it is not currently in view (defaults to true)
17900 * @return {Boolean} True if the value matched an item in the list, else false
17902 selectByValue : function(v, scrollIntoView){
17903 if(v !== undefined && v !== null){
17904 var r = this.findRecord(this.valueField || this.displayField, v);
17906 this.select(this.store.indexOf(r), scrollIntoView);
17914 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17915 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17916 * @param {Number} index The zero-based index of the list item to select
17917 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17918 * selected item if it is not currently in view (defaults to true)
17920 select : function(index, scrollIntoView){
17921 this.selectedIndex = index;
17922 this.view.select(index);
17923 if(scrollIntoView !== false){
17924 var el = this.view.getNode(index);
17926 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17929 this.list.scrollChildIntoView(el, false);
17935 selectNext : function(){
17936 var ct = this.store.getCount();
17938 if(this.selectedIndex == -1){
17940 }else if(this.selectedIndex < ct-1){
17941 this.select(this.selectedIndex+1);
17947 selectPrev : function(){
17948 var ct = this.store.getCount();
17950 if(this.selectedIndex == -1){
17952 }else if(this.selectedIndex != 0){
17953 this.select(this.selectedIndex-1);
17959 onKeyUp : function(e){
17960 if(this.editable !== false && !e.isSpecialKey()){
17961 this.lastKey = e.getKey();
17962 this.dqTask.delay(this.queryDelay);
17967 validateBlur : function(){
17968 return !this.list || !this.list.isVisible();
17972 initQuery : function(){
17974 var v = this.getRawValue();
17976 if(this.tickable && this.editable){
17977 v = this.tickableInputEl().getValue();
17984 doForce : function(){
17985 if(this.inputEl().dom.value.length > 0){
17986 this.inputEl().dom.value =
17987 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17993 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17994 * query allowing the query action to be canceled if needed.
17995 * @param {String} query The SQL query to execute
17996 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17997 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17998 * saved in the current store (defaults to false)
18000 doQuery : function(q, forceAll){
18002 if(q === undefined || q === null){
18007 forceAll: forceAll,
18011 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18016 forceAll = qe.forceAll;
18017 if(forceAll === true || (q.length >= this.minChars)){
18019 this.hasQuery = true;
18021 if(this.lastQuery != q || this.alwaysQuery){
18022 this.lastQuery = q;
18023 if(this.mode == 'local'){
18024 this.selectedIndex = -1;
18026 this.store.clearFilter();
18029 if(this.specialFilter){
18030 this.fireEvent('specialfilter', this);
18035 this.store.filter(this.displayField, q);
18038 this.store.fireEvent("datachanged", this.store);
18045 this.store.baseParams[this.queryParam] = q;
18047 var options = {params : this.getParams(q)};
18050 options.add = true;
18051 options.params.start = this.page * this.pageSize;
18054 this.store.load(options);
18057 * this code will make the page width larger, at the beginning, the list not align correctly,
18058 * we should expand the list on onLoad
18059 * so command out it
18064 this.selectedIndex = -1;
18069 this.loadNext = false;
18073 getParams : function(q){
18075 //p[this.queryParam] = q;
18079 p.limit = this.pageSize;
18085 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18087 collapse : function(){
18088 if(!this.isExpanded()){
18094 this.hasFocus = false;
18098 this.cancelBtn.hide();
18099 this.trigger.show();
18102 this.tickableInputEl().dom.value = '';
18103 this.tickableInputEl().blur();
18108 Roo.get(document).un('mousedown', this.collapseIf, this);
18109 Roo.get(document).un('mousewheel', this.collapseIf, this);
18110 if (!this.editable) {
18111 Roo.get(document).un('keydown', this.listKeyPress, this);
18113 this.fireEvent('collapse', this);
18119 collapseIf : function(e){
18120 var in_combo = e.within(this.el);
18121 var in_list = e.within(this.list);
18122 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18124 if (in_combo || in_list || is_list) {
18125 //e.stopPropagation();
18130 this.onTickableFooterButtonClick(e, false, false);
18138 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18140 expand : function(){
18142 if(this.isExpanded() || !this.hasFocus){
18146 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18147 this.list.setWidth(lw);
18153 this.restrictHeight();
18157 this.tickItems = Roo.apply([], this.item);
18160 this.cancelBtn.show();
18161 this.trigger.hide();
18164 this.tickableInputEl().focus();
18169 Roo.get(document).on('mousedown', this.collapseIf, this);
18170 Roo.get(document).on('mousewheel', this.collapseIf, this);
18171 if (!this.editable) {
18172 Roo.get(document).on('keydown', this.listKeyPress, this);
18175 this.fireEvent('expand', this);
18179 // Implements the default empty TriggerField.onTriggerClick function
18180 onTriggerClick : function(e)
18182 Roo.log('trigger click');
18184 if(this.disabled || !this.triggerList){
18189 this.loadNext = false;
18191 if(this.isExpanded()){
18193 if (!this.blockFocus) {
18194 this.inputEl().focus();
18198 this.hasFocus = true;
18199 if(this.triggerAction == 'all') {
18200 this.doQuery(this.allQuery, true);
18202 this.doQuery(this.getRawValue());
18204 if (!this.blockFocus) {
18205 this.inputEl().focus();
18210 onTickableTriggerClick : function(e)
18217 this.loadNext = false;
18218 this.hasFocus = true;
18220 if(this.triggerAction == 'all') {
18221 this.doQuery(this.allQuery, true);
18223 this.doQuery(this.getRawValue());
18227 onSearchFieldClick : function(e)
18229 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18230 this.onTickableFooterButtonClick(e, false, false);
18234 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18239 this.loadNext = false;
18240 this.hasFocus = true;
18242 if(this.triggerAction == 'all') {
18243 this.doQuery(this.allQuery, true);
18245 this.doQuery(this.getRawValue());
18249 listKeyPress : function(e)
18251 //Roo.log('listkeypress');
18252 // scroll to first matching element based on key pres..
18253 if (e.isSpecialKey()) {
18256 var k = String.fromCharCode(e.getKey()).toUpperCase();
18259 var csel = this.view.getSelectedNodes();
18260 var cselitem = false;
18262 var ix = this.view.indexOf(csel[0]);
18263 cselitem = this.store.getAt(ix);
18264 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18270 this.store.each(function(v) {
18272 // start at existing selection.
18273 if (cselitem.id == v.id) {
18279 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18280 match = this.store.indexOf(v);
18286 if (match === false) {
18287 return true; // no more action?
18290 this.view.select(match);
18291 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18292 sn.scrollIntoView(sn.dom.parentNode, false);
18295 onViewScroll : function(e, t){
18297 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){
18301 this.hasQuery = true;
18303 this.loading = this.list.select('.loading', true).first();
18305 if(this.loading === null){
18306 this.list.createChild({
18308 cls: 'loading roo-select2-more-results roo-select2-active',
18309 html: 'Loading more results...'
18312 this.loading = this.list.select('.loading', true).first();
18314 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18316 this.loading.hide();
18319 this.loading.show();
18324 this.loadNext = true;
18326 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18331 addItem : function(o)
18333 var dv = ''; // display value
18335 if (this.displayField) {
18336 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18338 // this is an error condition!!!
18339 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18346 var choice = this.choices.createChild({
18348 cls: 'roo-select2-search-choice',
18357 cls: 'roo-select2-search-choice-close fa fa-times',
18362 }, this.searchField);
18364 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18366 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18374 this.inputEl().dom.value = '';
18379 onRemoveItem : function(e, _self, o)
18381 e.preventDefault();
18383 this.lastItem = Roo.apply([], this.item);
18385 var index = this.item.indexOf(o.data) * 1;
18388 Roo.log('not this item?!');
18392 this.item.splice(index, 1);
18397 this.fireEvent('remove', this, e);
18403 syncValue : function()
18405 if(!this.item.length){
18412 Roo.each(this.item, function(i){
18413 if(_this.valueField){
18414 value.push(i[_this.valueField]);
18421 this.value = value.join(',');
18423 if(this.hiddenField){
18424 this.hiddenField.dom.value = this.value;
18427 this.store.fireEvent("datachanged", this.store);
18432 clearItem : function()
18434 if(!this.multiple){
18440 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18448 if(this.tickable && !Roo.isTouch){
18449 this.view.refresh();
18453 inputEl: function ()
18455 if(Roo.isIOS && this.useNativeIOS){
18456 return this.el.select('select.roo-ios-select', true).first();
18459 if(Roo.isTouch && this.mobileTouchView){
18460 return this.el.select('input.form-control',true).first();
18464 return this.searchField;
18467 return this.el.select('input.form-control',true).first();
18470 onTickableFooterButtonClick : function(e, btn, el)
18472 e.preventDefault();
18474 this.lastItem = Roo.apply([], this.item);
18476 if(btn && btn.name == 'cancel'){
18477 this.tickItems = Roo.apply([], this.item);
18486 Roo.each(this.tickItems, function(o){
18494 validate : function()
18496 if(this.getVisibilityEl().hasClass('hidden')){
18500 var v = this.getRawValue();
18503 v = this.getValue();
18506 if(this.disabled || this.allowBlank || v.length){
18511 this.markInvalid();
18515 tickableInputEl : function()
18517 if(!this.tickable || !this.editable){
18518 return this.inputEl();
18521 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18525 getAutoCreateTouchView : function()
18530 cls: 'form-group' //input-group
18536 type : this.inputType,
18537 cls : 'form-control x-combo-noedit',
18538 autocomplete: 'new-password',
18539 placeholder : this.placeholder || '',
18544 input.name = this.name;
18548 input.cls += ' input-' + this.size;
18551 if (this.disabled) {
18552 input.disabled = true;
18556 cls : 'roo-combobox-wrap',
18563 inputblock.cls += ' input-group';
18565 inputblock.cn.unshift({
18567 cls : 'input-group-addon input-group-prepend input-group-text',
18572 if(this.removable && !this.multiple){
18573 inputblock.cls += ' roo-removable';
18575 inputblock.cn.push({
18578 cls : 'roo-combo-removable-btn close'
18582 if(this.hasFeedback && !this.allowBlank){
18584 inputblock.cls += ' has-feedback';
18586 inputblock.cn.push({
18588 cls: 'glyphicon form-control-feedback'
18595 inputblock.cls += (this.before) ? '' : ' input-group';
18597 inputblock.cn.push({
18599 cls : 'input-group-addon input-group-append input-group-text',
18605 var ibwrap = inputblock;
18610 cls: 'roo-select2-choices',
18614 cls: 'roo-select2-search-field',
18627 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18632 cls: 'form-hidden-field'
18638 if(!this.multiple && this.showToggleBtn){
18644 if (this.caret != false) {
18647 cls: 'fa fa-' + this.caret
18654 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18656 Roo.bootstrap.version == 3 ? caret : '',
18659 cls: 'combobox-clear',
18673 combobox.cls += ' roo-select2-container-multi';
18676 var required = this.allowBlank ? {
18678 style: 'display: none'
18681 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18682 tooltip : 'This field is required'
18685 var align = this.labelAlign || this.parentLabelAlign();
18687 if (align ==='left' && this.fieldLabel.length) {
18693 cls : 'control-label col-form-label',
18694 html : this.fieldLabel
18698 cls : 'roo-combobox-wrap ',
18705 var labelCfg = cfg.cn[1];
18706 var contentCfg = cfg.cn[2];
18709 if(this.indicatorpos == 'right'){
18714 cls : 'control-label col-form-label',
18718 html : this.fieldLabel
18724 cls : "roo-combobox-wrap ",
18732 labelCfg = cfg.cn[0];
18733 contentCfg = cfg.cn[1];
18738 if(this.labelWidth > 12){
18739 labelCfg.style = "width: " + this.labelWidth + 'px';
18742 if(this.labelWidth < 13 && this.labelmd == 0){
18743 this.labelmd = this.labelWidth;
18746 if(this.labellg > 0){
18747 labelCfg.cls += ' col-lg-' + this.labellg;
18748 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18751 if(this.labelmd > 0){
18752 labelCfg.cls += ' col-md-' + this.labelmd;
18753 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18756 if(this.labelsm > 0){
18757 labelCfg.cls += ' col-sm-' + this.labelsm;
18758 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18761 if(this.labelxs > 0){
18762 labelCfg.cls += ' col-xs-' + this.labelxs;
18763 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18767 } else if ( this.fieldLabel.length) {
18772 cls : 'control-label',
18773 html : this.fieldLabel
18784 if(this.indicatorpos == 'right'){
18788 cls : 'control-label',
18789 html : this.fieldLabel,
18807 var settings = this;
18809 ['xs','sm','md','lg'].map(function(size){
18810 if (settings[size]) {
18811 cfg.cls += ' col-' + size + '-' + settings[size];
18818 initTouchView : function()
18820 this.renderTouchView();
18822 this.touchViewEl.on('scroll', function(){
18823 this.el.dom.scrollTop = 0;
18826 this.originalValue = this.getValue();
18828 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18830 this.inputEl().on("click", this.showTouchView, this);
18831 if (this.triggerEl) {
18832 this.triggerEl.on("click", this.showTouchView, this);
18836 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18837 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18839 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18841 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18842 this.store.on('load', this.onTouchViewLoad, this);
18843 this.store.on('loadexception', this.onTouchViewLoadException, this);
18845 if(this.hiddenName){
18847 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18849 this.hiddenField.dom.value =
18850 this.hiddenValue !== undefined ? this.hiddenValue :
18851 this.value !== undefined ? this.value : '';
18853 this.el.dom.removeAttribute('name');
18854 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18858 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18859 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18862 if(this.removable && !this.multiple){
18863 var close = this.closeTriggerEl();
18865 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18866 close.on('click', this.removeBtnClick, this, close);
18870 * fix the bug in Safari iOS8
18872 this.inputEl().on("focus", function(e){
18873 document.activeElement.blur();
18876 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18883 renderTouchView : function()
18885 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18886 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18888 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18889 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18892 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18893 this.touchViewBodyEl.setStyle('overflow', 'auto');
18895 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18896 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18898 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18899 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18903 showTouchView : function()
18909 this.touchViewHeaderEl.hide();
18911 if(this.modalTitle.length){
18912 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18913 this.touchViewHeaderEl.show();
18916 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18917 this.touchViewEl.show();
18919 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18921 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18922 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18924 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18926 if(this.modalTitle.length){
18927 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18930 this.touchViewBodyEl.setHeight(bodyHeight);
18934 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18936 this.touchViewEl.addClass(['in','show']);
18939 if(this._touchViewMask){
18940 Roo.get(document.body).addClass("x-body-masked");
18941 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18942 this._touchViewMask.setStyle('z-index', 10000);
18943 this._touchViewMask.addClass('show');
18946 this.doTouchViewQuery();
18950 hideTouchView : function()
18952 this.touchViewEl.removeClass(['in','show']);
18956 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18958 this.touchViewEl.setStyle('display', 'none');
18961 if(this._touchViewMask){
18962 this._touchViewMask.removeClass('show');
18963 Roo.get(document.body).removeClass("x-body-masked");
18967 setTouchViewValue : function()
18974 Roo.each(this.tickItems, function(o){
18979 this.hideTouchView();
18982 doTouchViewQuery : function()
18991 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18995 if(!this.alwaysQuery || this.mode == 'local'){
18996 this.onTouchViewLoad();
19003 onTouchViewBeforeLoad : function(combo,opts)
19009 onTouchViewLoad : function()
19011 if(this.store.getCount() < 1){
19012 this.onTouchViewEmptyResults();
19016 this.clearTouchView();
19018 var rawValue = this.getRawValue();
19020 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19022 this.tickItems = [];
19024 this.store.data.each(function(d, rowIndex){
19025 var row = this.touchViewListGroup.createChild(template);
19027 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19028 row.addClass(d.data.cls);
19031 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19034 html : d.data[this.displayField]
19037 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19038 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19041 row.removeClass('selected');
19042 if(!this.multiple && this.valueField &&
19043 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19046 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19047 row.addClass('selected');
19050 if(this.multiple && this.valueField &&
19051 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19055 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19056 this.tickItems.push(d.data);
19059 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19063 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19065 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19067 if(this.modalTitle.length){
19068 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19071 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19073 if(this.mobile_restrict_height && listHeight < bodyHeight){
19074 this.touchViewBodyEl.setHeight(listHeight);
19079 if(firstChecked && listHeight > bodyHeight){
19080 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19085 onTouchViewLoadException : function()
19087 this.hideTouchView();
19090 onTouchViewEmptyResults : function()
19092 this.clearTouchView();
19094 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19096 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19100 clearTouchView : function()
19102 this.touchViewListGroup.dom.innerHTML = '';
19105 onTouchViewClick : function(e, el, o)
19107 e.preventDefault();
19110 var rowIndex = o.rowIndex;
19112 var r = this.store.getAt(rowIndex);
19114 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19116 if(!this.multiple){
19117 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19118 c.dom.removeAttribute('checked');
19121 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19123 this.setFromData(r.data);
19125 var close = this.closeTriggerEl();
19131 this.hideTouchView();
19133 this.fireEvent('select', this, r, rowIndex);
19138 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19139 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19140 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19144 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19145 this.addItem(r.data);
19146 this.tickItems.push(r.data);
19150 getAutoCreateNativeIOS : function()
19153 cls: 'form-group' //input-group,
19158 cls : 'roo-ios-select'
19162 combobox.name = this.name;
19165 if (this.disabled) {
19166 combobox.disabled = true;
19169 var settings = this;
19171 ['xs','sm','md','lg'].map(function(size){
19172 if (settings[size]) {
19173 cfg.cls += ' col-' + size + '-' + settings[size];
19183 initIOSView : function()
19185 this.store.on('load', this.onIOSViewLoad, this);
19190 onIOSViewLoad : function()
19192 if(this.store.getCount() < 1){
19196 this.clearIOSView();
19198 if(this.allowBlank) {
19200 var default_text = '-- SELECT --';
19202 if(this.placeholder.length){
19203 default_text = this.placeholder;
19206 if(this.emptyTitle.length){
19207 default_text += ' - ' + this.emptyTitle + ' -';
19210 var opt = this.inputEl().createChild({
19213 html : default_text
19217 o[this.valueField] = 0;
19218 o[this.displayField] = default_text;
19220 this.ios_options.push({
19227 this.store.data.each(function(d, rowIndex){
19231 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19232 html = d.data[this.displayField];
19237 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19238 value = d.data[this.valueField];
19247 if(this.value == d.data[this.valueField]){
19248 option['selected'] = true;
19251 var opt = this.inputEl().createChild(option);
19253 this.ios_options.push({
19260 this.inputEl().on('change', function(){
19261 this.fireEvent('select', this);
19266 clearIOSView: function()
19268 this.inputEl().dom.innerHTML = '';
19270 this.ios_options = [];
19273 setIOSValue: function(v)
19277 if(!this.ios_options){
19281 Roo.each(this.ios_options, function(opts){
19283 opts.el.dom.removeAttribute('selected');
19285 if(opts.data[this.valueField] != v){
19289 opts.el.dom.setAttribute('selected', true);
19295 * @cfg {Boolean} grow
19299 * @cfg {Number} growMin
19303 * @cfg {Number} growMax
19312 Roo.apply(Roo.bootstrap.ComboBox, {
19316 cls: 'modal-header',
19338 cls: 'list-group-item',
19342 cls: 'roo-combobox-list-group-item-value'
19346 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19360 listItemCheckbox : {
19362 cls: 'list-group-item',
19366 cls: 'roo-combobox-list-group-item-value'
19370 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19386 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19391 cls: 'modal-footer',
19399 cls: 'col-xs-6 text-left',
19402 cls: 'btn btn-danger roo-touch-view-cancel',
19408 cls: 'col-xs-6 text-right',
19411 cls: 'btn btn-success roo-touch-view-ok',
19422 Roo.apply(Roo.bootstrap.ComboBox, {
19424 touchViewTemplate : {
19426 cls: 'modal fade roo-combobox-touch-view',
19430 cls: 'modal-dialog',
19431 style : 'position:fixed', // we have to fix position....
19435 cls: 'modal-content',
19437 Roo.bootstrap.ComboBox.header,
19438 Roo.bootstrap.ComboBox.body,
19439 Roo.bootstrap.ComboBox.footer
19448 * Ext JS Library 1.1.1
19449 * Copyright(c) 2006-2007, Ext JS, LLC.
19451 * Originally Released Under LGPL - original licence link has changed is not relivant.
19454 * <script type="text/javascript">
19459 * @extends Roo.util.Observable
19460 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19461 * This class also supports single and multi selection modes. <br>
19462 * Create a data model bound view:
19464 var store = new Roo.data.Store(...);
19466 var view = new Roo.View({
19468 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19470 singleSelect: true,
19471 selectedClass: "ydataview-selected",
19475 // listen for node click?
19476 view.on("click", function(vw, index, node, e){
19477 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19481 dataModel.load("foobar.xml");
19483 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19485 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19486 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19488 * Note: old style constructor is still suported (container, template, config)
19491 * Create a new View
19492 * @param {Object} config The config object
19495 Roo.View = function(config, depreciated_tpl, depreciated_config){
19497 this.parent = false;
19499 if (typeof(depreciated_tpl) == 'undefined') {
19500 // new way.. - universal constructor.
19501 Roo.apply(this, config);
19502 this.el = Roo.get(this.el);
19505 this.el = Roo.get(config);
19506 this.tpl = depreciated_tpl;
19507 Roo.apply(this, depreciated_config);
19509 this.wrapEl = this.el.wrap().wrap();
19510 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19513 if(typeof(this.tpl) == "string"){
19514 this.tpl = new Roo.Template(this.tpl);
19516 // support xtype ctors..
19517 this.tpl = new Roo.factory(this.tpl, Roo);
19521 this.tpl.compile();
19526 * @event beforeclick
19527 * Fires before a click is processed. Returns false to cancel the default action.
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
19533 "beforeclick" : true,
19536 * Fires when a template node is 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
19545 * Fires when a template node is double 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
19553 * @event contextmenu
19554 * Fires when a template node is right clicked.
19555 * @param {Roo.View} this
19556 * @param {Number} index The index of the target node
19557 * @param {HTMLElement} node The target node
19558 * @param {Roo.EventObject} e The raw event object
19560 "contextmenu" : true,
19562 * @event selectionchange
19563 * Fires when the selected nodes change.
19564 * @param {Roo.View} this
19565 * @param {Array} selections Array of the selected nodes
19567 "selectionchange" : true,
19570 * @event beforeselect
19571 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19572 * @param {Roo.View} this
19573 * @param {HTMLElement} node The node to be selected
19574 * @param {Array} selections Array of currently selected nodes
19576 "beforeselect" : true,
19578 * @event preparedata
19579 * Fires on every row to render, to allow you to change the data.
19580 * @param {Roo.View} this
19581 * @param {Object} data to be rendered (change this)
19583 "preparedata" : true
19591 "click": this.onClick,
19592 "dblclick": this.onDblClick,
19593 "contextmenu": this.onContextMenu,
19597 this.selections = [];
19599 this.cmp = new Roo.CompositeElementLite([]);
19601 this.store = Roo.factory(this.store, Roo.data);
19602 this.setStore(this.store, true);
19605 if ( this.footer && this.footer.xtype) {
19607 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19609 this.footer.dataSource = this.store;
19610 this.footer.container = fctr;
19611 this.footer = Roo.factory(this.footer, Roo);
19612 fctr.insertFirst(this.el);
19614 // this is a bit insane - as the paging toolbar seems to detach the el..
19615 // dom.parentNode.parentNode.parentNode
19616 // they get detached?
19620 Roo.View.superclass.constructor.call(this);
19625 Roo.extend(Roo.View, Roo.util.Observable, {
19628 * @cfg {Roo.data.Store} store Data store to load data from.
19633 * @cfg {String|Roo.Element} el The container element.
19638 * @cfg {String|Roo.Template} tpl The template used by this View
19642 * @cfg {String} dataName the named area of the template to use as the data area
19643 * Works with domtemplates roo-name="name"
19647 * @cfg {String} selectedClass The css class to add to selected nodes
19649 selectedClass : "x-view-selected",
19651 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19656 * @cfg {String} text to display on mask (default Loading)
19660 * @cfg {Boolean} multiSelect Allow multiple selection
19662 multiSelect : false,
19664 * @cfg {Boolean} singleSelect Allow single selection
19666 singleSelect: false,
19669 * @cfg {Boolean} toggleSelect - selecting
19671 toggleSelect : false,
19674 * @cfg {Boolean} tickable - selecting
19679 * Returns the element this view is bound to.
19680 * @return {Roo.Element}
19682 getEl : function(){
19683 return this.wrapEl;
19689 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19691 refresh : function(){
19692 //Roo.log('refresh');
19695 // if we are using something like 'domtemplate', then
19696 // the what gets used is:
19697 // t.applySubtemplate(NAME, data, wrapping data..)
19698 // the outer template then get' applied with
19699 // the store 'extra data'
19700 // and the body get's added to the
19701 // roo-name="data" node?
19702 // <span class='roo-tpl-{name}'></span> ?????
19706 this.clearSelections();
19707 this.el.update("");
19709 var records = this.store.getRange();
19710 if(records.length < 1) {
19712 // is this valid?? = should it render a template??
19714 this.el.update(this.emptyText);
19718 if (this.dataName) {
19719 this.el.update(t.apply(this.store.meta)); //????
19720 el = this.el.child('.roo-tpl-' + this.dataName);
19723 for(var i = 0, len = records.length; i < len; i++){
19724 var data = this.prepareData(records[i].data, i, records[i]);
19725 this.fireEvent("preparedata", this, data, i, records[i]);
19727 var d = Roo.apply({}, data);
19730 Roo.apply(d, {'roo-id' : Roo.id()});
19734 Roo.each(this.parent.item, function(item){
19735 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19738 Roo.apply(d, {'roo-data-checked' : 'checked'});
19742 html[html.length] = Roo.util.Format.trim(
19744 t.applySubtemplate(this.dataName, d, this.store.meta) :
19751 el.update(html.join(""));
19752 this.nodes = el.dom.childNodes;
19753 this.updateIndexes(0);
19758 * Function to override to reformat the data that is sent to
19759 * the template for each node.
19760 * DEPRICATED - use the preparedata event handler.
19761 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19762 * a JSON object for an UpdateManager bound view).
19764 prepareData : function(data, index, record)
19766 this.fireEvent("preparedata", this, data, index, record);
19770 onUpdate : function(ds, record){
19771 // Roo.log('on update');
19772 this.clearSelections();
19773 var index = this.store.indexOf(record);
19774 var n = this.nodes[index];
19775 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19776 n.parentNode.removeChild(n);
19777 this.updateIndexes(index, index);
19783 onAdd : function(ds, records, index)
19785 //Roo.log(['on Add', ds, records, index] );
19786 this.clearSelections();
19787 if(this.nodes.length == 0){
19791 var n = this.nodes[index];
19792 for(var i = 0, len = records.length; i < len; i++){
19793 var d = this.prepareData(records[i].data, i, records[i]);
19795 this.tpl.insertBefore(n, d);
19798 this.tpl.append(this.el, d);
19801 this.updateIndexes(index);
19804 onRemove : function(ds, record, index){
19805 // Roo.log('onRemove');
19806 this.clearSelections();
19807 var el = this.dataName ?
19808 this.el.child('.roo-tpl-' + this.dataName) :
19811 el.dom.removeChild(this.nodes[index]);
19812 this.updateIndexes(index);
19816 * Refresh an individual node.
19817 * @param {Number} index
19819 refreshNode : function(index){
19820 this.onUpdate(this.store, this.store.getAt(index));
19823 updateIndexes : function(startIndex, endIndex){
19824 var ns = this.nodes;
19825 startIndex = startIndex || 0;
19826 endIndex = endIndex || ns.length - 1;
19827 for(var i = startIndex; i <= endIndex; i++){
19828 ns[i].nodeIndex = i;
19833 * Changes the data store this view uses and refresh the view.
19834 * @param {Store} store
19836 setStore : function(store, initial){
19837 if(!initial && this.store){
19838 this.store.un("datachanged", this.refresh);
19839 this.store.un("add", this.onAdd);
19840 this.store.un("remove", this.onRemove);
19841 this.store.un("update", this.onUpdate);
19842 this.store.un("clear", this.refresh);
19843 this.store.un("beforeload", this.onBeforeLoad);
19844 this.store.un("load", this.onLoad);
19845 this.store.un("loadexception", this.onLoad);
19849 store.on("datachanged", this.refresh, this);
19850 store.on("add", this.onAdd, this);
19851 store.on("remove", this.onRemove, this);
19852 store.on("update", this.onUpdate, this);
19853 store.on("clear", this.refresh, this);
19854 store.on("beforeload", this.onBeforeLoad, this);
19855 store.on("load", this.onLoad, this);
19856 store.on("loadexception", this.onLoad, this);
19864 * onbeforeLoad - masks the loading area.
19867 onBeforeLoad : function(store,opts)
19869 //Roo.log('onBeforeLoad');
19871 this.el.update("");
19873 this.el.mask(this.mask ? this.mask : "Loading" );
19875 onLoad : function ()
19882 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19883 * @param {HTMLElement} node
19884 * @return {HTMLElement} The template node
19886 findItemFromChild : function(node){
19887 var el = this.dataName ?
19888 this.el.child('.roo-tpl-' + this.dataName,true) :
19891 if(!node || node.parentNode == el){
19894 var p = node.parentNode;
19895 while(p && p != el){
19896 if(p.parentNode == el){
19905 onClick : function(e){
19906 var item = this.findItemFromChild(e.getTarget());
19908 var index = this.indexOf(item);
19909 if(this.onItemClick(item, index, e) !== false){
19910 this.fireEvent("click", this, index, item, e);
19913 this.clearSelections();
19918 onContextMenu : function(e){
19919 var item = this.findItemFromChild(e.getTarget());
19921 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19926 onDblClick : function(e){
19927 var item = this.findItemFromChild(e.getTarget());
19929 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19933 onItemClick : function(item, index, e)
19935 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19938 if (this.toggleSelect) {
19939 var m = this.isSelected(item) ? 'unselect' : 'select';
19942 _t[m](item, true, false);
19945 if(this.multiSelect || this.singleSelect){
19946 if(this.multiSelect && e.shiftKey && this.lastSelection){
19947 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19949 this.select(item, this.multiSelect && e.ctrlKey);
19950 this.lastSelection = item;
19953 if(!this.tickable){
19954 e.preventDefault();
19962 * Get the number of selected nodes.
19965 getSelectionCount : function(){
19966 return this.selections.length;
19970 * Get the currently selected nodes.
19971 * @return {Array} An array of HTMLElements
19973 getSelectedNodes : function(){
19974 return this.selections;
19978 * Get the indexes of the selected nodes.
19981 getSelectedIndexes : function(){
19982 var indexes = [], s = this.selections;
19983 for(var i = 0, len = s.length; i < len; i++){
19984 indexes.push(s[i].nodeIndex);
19990 * Clear all selections
19991 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19993 clearSelections : function(suppressEvent){
19994 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19995 this.cmp.elements = this.selections;
19996 this.cmp.removeClass(this.selectedClass);
19997 this.selections = [];
19998 if(!suppressEvent){
19999 this.fireEvent("selectionchange", this, this.selections);
20005 * Returns true if the passed node is selected
20006 * @param {HTMLElement/Number} node The node or node index
20007 * @return {Boolean}
20009 isSelected : function(node){
20010 var s = this.selections;
20014 node = this.getNode(node);
20015 return s.indexOf(node) !== -1;
20020 * @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
20021 * @param {Boolean} keepExisting (optional) true to keep existing selections
20022 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20024 select : function(nodeInfo, keepExisting, suppressEvent){
20025 if(nodeInfo instanceof Array){
20027 this.clearSelections(true);
20029 for(var i = 0, len = nodeInfo.length; i < len; i++){
20030 this.select(nodeInfo[i], true, true);
20034 var node = this.getNode(nodeInfo);
20035 if(!node || this.isSelected(node)){
20036 return; // already selected.
20039 this.clearSelections(true);
20042 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20043 Roo.fly(node).addClass(this.selectedClass);
20044 this.selections.push(node);
20045 if(!suppressEvent){
20046 this.fireEvent("selectionchange", this, this.selections);
20054 * @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
20055 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20056 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20058 unselect : function(nodeInfo, keepExisting, suppressEvent)
20060 if(nodeInfo instanceof Array){
20061 Roo.each(this.selections, function(s) {
20062 this.unselect(s, nodeInfo);
20066 var node = this.getNode(nodeInfo);
20067 if(!node || !this.isSelected(node)){
20068 //Roo.log("not selected");
20069 return; // not selected.
20073 Roo.each(this.selections, function(s) {
20075 Roo.fly(node).removeClass(this.selectedClass);
20082 this.selections= ns;
20083 this.fireEvent("selectionchange", this, this.selections);
20087 * Gets a template node.
20088 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20089 * @return {HTMLElement} The node or null if it wasn't found
20091 getNode : function(nodeInfo){
20092 if(typeof nodeInfo == "string"){
20093 return document.getElementById(nodeInfo);
20094 }else if(typeof nodeInfo == "number"){
20095 return this.nodes[nodeInfo];
20101 * Gets a range template nodes.
20102 * @param {Number} startIndex
20103 * @param {Number} endIndex
20104 * @return {Array} An array of nodes
20106 getNodes : function(start, end){
20107 var ns = this.nodes;
20108 start = start || 0;
20109 end = typeof end == "undefined" ? ns.length - 1 : end;
20112 for(var i = start; i <= end; i++){
20116 for(var i = start; i >= end; i--){
20124 * Finds the index of the passed node
20125 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20126 * @return {Number} The index of the node or -1
20128 indexOf : function(node){
20129 node = this.getNode(node);
20130 if(typeof node.nodeIndex == "number"){
20131 return node.nodeIndex;
20133 var ns = this.nodes;
20134 for(var i = 0, len = ns.length; i < len; i++){
20145 * based on jquery fullcalendar
20149 Roo.bootstrap = Roo.bootstrap || {};
20151 * @class Roo.bootstrap.Calendar
20152 * @extends Roo.bootstrap.Component
20153 * Bootstrap Calendar class
20154 * @cfg {Boolean} loadMask (true|false) default false
20155 * @cfg {Object} header generate the user specific header of the calendar, default false
20158 * Create a new Container
20159 * @param {Object} config The config object
20164 Roo.bootstrap.Calendar = function(config){
20165 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20169 * Fires when a date is selected
20170 * @param {DatePicker} this
20171 * @param {Date} date The selected date
20175 * @event monthchange
20176 * Fires when the displayed month changes
20177 * @param {DatePicker} this
20178 * @param {Date} date The selected month
20180 'monthchange': true,
20182 * @event evententer
20183 * Fires when mouse over an event
20184 * @param {Calendar} this
20185 * @param {event} Event
20187 'evententer': true,
20189 * @event eventleave
20190 * Fires when the mouse leaves an
20191 * @param {Calendar} this
20194 'eventleave': true,
20196 * @event eventclick
20197 * Fires when the mouse click an
20198 * @param {Calendar} this
20207 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20210 * @cfg {Roo.data.Store} store
20211 * The data source for the calendar
20215 * @cfg {Number} startDay
20216 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20224 getAutoCreate : function(){
20227 var fc_button = function(name, corner, style, content ) {
20228 return Roo.apply({},{
20230 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20232 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20235 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20246 style : 'width:100%',
20253 cls : 'fc-header-left',
20255 fc_button('prev', 'left', 'arrow', '‹' ),
20256 fc_button('next', 'right', 'arrow', '›' ),
20257 { tag: 'span', cls: 'fc-header-space' },
20258 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20266 cls : 'fc-header-center',
20270 cls: 'fc-header-title',
20273 html : 'month / year'
20281 cls : 'fc-header-right',
20283 /* fc_button('month', 'left', '', 'month' ),
20284 fc_button('week', '', '', 'week' ),
20285 fc_button('day', 'right', '', 'day' )
20297 header = this.header;
20300 var cal_heads = function() {
20302 // fixme - handle this.
20304 for (var i =0; i < Date.dayNames.length; i++) {
20305 var d = Date.dayNames[i];
20308 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20309 html : d.substring(0,3)
20313 ret[0].cls += ' fc-first';
20314 ret[6].cls += ' fc-last';
20317 var cal_cell = function(n) {
20320 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20325 cls: 'fc-day-number',
20329 cls: 'fc-day-content',
20333 style: 'position: relative;' // height: 17px;
20345 var cal_rows = function() {
20348 for (var r = 0; r < 6; r++) {
20355 for (var i =0; i < Date.dayNames.length; i++) {
20356 var d = Date.dayNames[i];
20357 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20360 row.cn[0].cls+=' fc-first';
20361 row.cn[0].cn[0].style = 'min-height:90px';
20362 row.cn[6].cls+=' fc-last';
20366 ret[0].cls += ' fc-first';
20367 ret[4].cls += ' fc-prev-last';
20368 ret[5].cls += ' fc-last';
20375 cls: 'fc-border-separate',
20376 style : 'width:100%',
20384 cls : 'fc-first fc-last',
20402 cls : 'fc-content',
20403 style : "position: relative;",
20406 cls : 'fc-view fc-view-month fc-grid',
20407 style : 'position: relative',
20408 unselectable : 'on',
20411 cls : 'fc-event-container',
20412 style : 'position:absolute;z-index:8;top:0;left:0;'
20430 initEvents : function()
20433 throw "can not find store for calendar";
20439 style: "text-align:center",
20443 style: "background-color:white;width:50%;margin:250 auto",
20447 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20458 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20460 var size = this.el.select('.fc-content', true).first().getSize();
20461 this.maskEl.setSize(size.width, size.height);
20462 this.maskEl.enableDisplayMode("block");
20463 if(!this.loadMask){
20464 this.maskEl.hide();
20467 this.store = Roo.factory(this.store, Roo.data);
20468 this.store.on('load', this.onLoad, this);
20469 this.store.on('beforeload', this.onBeforeLoad, this);
20473 this.cells = this.el.select('.fc-day',true);
20474 //Roo.log(this.cells);
20475 this.textNodes = this.el.query('.fc-day-number');
20476 this.cells.addClassOnOver('fc-state-hover');
20478 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20479 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20480 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20481 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20483 this.on('monthchange', this.onMonthChange, this);
20485 this.update(new Date().clearTime());
20488 resize : function() {
20489 var sz = this.el.getSize();
20491 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20492 this.el.select('.fc-day-content div',true).setHeight(34);
20497 showPrevMonth : function(e){
20498 this.update(this.activeDate.add("mo", -1));
20500 showToday : function(e){
20501 this.update(new Date().clearTime());
20504 showNextMonth : function(e){
20505 this.update(this.activeDate.add("mo", 1));
20509 showPrevYear : function(){
20510 this.update(this.activeDate.add("y", -1));
20514 showNextYear : function(){
20515 this.update(this.activeDate.add("y", 1));
20520 update : function(date)
20522 var vd = this.activeDate;
20523 this.activeDate = date;
20524 // if(vd && this.el){
20525 // var t = date.getTime();
20526 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20527 // Roo.log('using add remove');
20529 // this.fireEvent('monthchange', this, date);
20531 // this.cells.removeClass("fc-state-highlight");
20532 // this.cells.each(function(c){
20533 // if(c.dateValue == t){
20534 // c.addClass("fc-state-highlight");
20535 // setTimeout(function(){
20536 // try{c.dom.firstChild.focus();}catch(e){}
20546 var days = date.getDaysInMonth();
20548 var firstOfMonth = date.getFirstDateOfMonth();
20549 var startingPos = firstOfMonth.getDay()-this.startDay;
20551 if(startingPos < this.startDay){
20555 var pm = date.add(Date.MONTH, -1);
20556 var prevStart = pm.getDaysInMonth()-startingPos;
20558 this.cells = this.el.select('.fc-day',true);
20559 this.textNodes = this.el.query('.fc-day-number');
20560 this.cells.addClassOnOver('fc-state-hover');
20562 var cells = this.cells.elements;
20563 var textEls = this.textNodes;
20565 Roo.each(cells, function(cell){
20566 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20569 days += startingPos;
20571 // convert everything to numbers so it's fast
20572 var day = 86400000;
20573 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20576 //Roo.log(prevStart);
20578 var today = new Date().clearTime().getTime();
20579 var sel = date.clearTime().getTime();
20580 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20581 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20582 var ddMatch = this.disabledDatesRE;
20583 var ddText = this.disabledDatesText;
20584 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20585 var ddaysText = this.disabledDaysText;
20586 var format = this.format;
20588 var setCellClass = function(cal, cell){
20592 //Roo.log('set Cell Class');
20594 var t = d.getTime();
20598 cell.dateValue = t;
20600 cell.className += " fc-today";
20601 cell.className += " fc-state-highlight";
20602 cell.title = cal.todayText;
20605 // disable highlight in other month..
20606 //cell.className += " fc-state-highlight";
20611 cell.className = " fc-state-disabled";
20612 cell.title = cal.minText;
20616 cell.className = " fc-state-disabled";
20617 cell.title = cal.maxText;
20621 if(ddays.indexOf(d.getDay()) != -1){
20622 cell.title = ddaysText;
20623 cell.className = " fc-state-disabled";
20626 if(ddMatch && format){
20627 var fvalue = d.dateFormat(format);
20628 if(ddMatch.test(fvalue)){
20629 cell.title = ddText.replace("%0", fvalue);
20630 cell.className = " fc-state-disabled";
20634 if (!cell.initialClassName) {
20635 cell.initialClassName = cell.dom.className;
20638 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20643 for(; i < startingPos; i++) {
20644 textEls[i].innerHTML = (++prevStart);
20645 d.setDate(d.getDate()+1);
20647 cells[i].className = "fc-past fc-other-month";
20648 setCellClass(this, cells[i]);
20653 for(; i < days; i++){
20654 intDay = i - startingPos + 1;
20655 textEls[i].innerHTML = (intDay);
20656 d.setDate(d.getDate()+1);
20658 cells[i].className = ''; // "x-date-active";
20659 setCellClass(this, cells[i]);
20663 for(; i < 42; i++) {
20664 textEls[i].innerHTML = (++extraDays);
20665 d.setDate(d.getDate()+1);
20667 cells[i].className = "fc-future fc-other-month";
20668 setCellClass(this, cells[i]);
20671 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20673 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20675 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20676 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20678 if(totalRows != 6){
20679 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20680 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20683 this.fireEvent('monthchange', this, date);
20687 if(!this.internalRender){
20688 var main = this.el.dom.firstChild;
20689 var w = main.offsetWidth;
20690 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20691 Roo.fly(main).setWidth(w);
20692 this.internalRender = true;
20693 // opera does not respect the auto grow header center column
20694 // then, after it gets a width opera refuses to recalculate
20695 // without a second pass
20696 if(Roo.isOpera && !this.secondPass){
20697 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20698 this.secondPass = true;
20699 this.update.defer(10, this, [date]);
20706 findCell : function(dt) {
20707 dt = dt.clearTime().getTime();
20709 this.cells.each(function(c){
20710 //Roo.log("check " +c.dateValue + '?=' + dt);
20711 if(c.dateValue == dt){
20721 findCells : function(ev) {
20722 var s = ev.start.clone().clearTime().getTime();
20724 var e= ev.end.clone().clearTime().getTime();
20727 this.cells.each(function(c){
20728 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20730 if(c.dateValue > e){
20733 if(c.dateValue < s){
20742 // findBestRow: function(cells)
20746 // for (var i =0 ; i < cells.length;i++) {
20747 // ret = Math.max(cells[i].rows || 0,ret);
20754 addItem : function(ev)
20756 // look for vertical location slot in
20757 var cells = this.findCells(ev);
20759 // ev.row = this.findBestRow(cells);
20761 // work out the location.
20765 for(var i =0; i < cells.length; i++) {
20767 cells[i].row = cells[0].row;
20770 cells[i].row = cells[i].row + 1;
20780 if (crow.start.getY() == cells[i].getY()) {
20782 crow.end = cells[i];
20799 cells[0].events.push(ev);
20801 this.calevents.push(ev);
20804 clearEvents: function() {
20806 if(!this.calevents){
20810 Roo.each(this.cells.elements, function(c){
20816 Roo.each(this.calevents, function(e) {
20817 Roo.each(e.els, function(el) {
20818 el.un('mouseenter' ,this.onEventEnter, this);
20819 el.un('mouseleave' ,this.onEventLeave, this);
20824 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20830 renderEvents: function()
20834 this.cells.each(function(c) {
20843 if(c.row != c.events.length){
20844 r = 4 - (4 - (c.row - c.events.length));
20847 c.events = ev.slice(0, r);
20848 c.more = ev.slice(r);
20850 if(c.more.length && c.more.length == 1){
20851 c.events.push(c.more.pop());
20854 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20858 this.cells.each(function(c) {
20860 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20863 for (var e = 0; e < c.events.length; e++){
20864 var ev = c.events[e];
20865 var rows = ev.rows;
20867 for(var i = 0; i < rows.length; i++) {
20869 // how many rows should it span..
20872 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20873 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20875 unselectable : "on",
20878 cls: 'fc-event-inner',
20882 // cls: 'fc-event-time',
20883 // html : cells.length > 1 ? '' : ev.time
20887 cls: 'fc-event-title',
20888 html : String.format('{0}', ev.title)
20895 cls: 'ui-resizable-handle ui-resizable-e',
20896 html : '  '
20903 cfg.cls += ' fc-event-start';
20905 if ((i+1) == rows.length) {
20906 cfg.cls += ' fc-event-end';
20909 var ctr = _this.el.select('.fc-event-container',true).first();
20910 var cg = ctr.createChild(cfg);
20912 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20913 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20915 var r = (c.more.length) ? 1 : 0;
20916 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20917 cg.setWidth(ebox.right - sbox.x -2);
20919 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20920 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20921 cg.on('click', _this.onEventClick, _this, ev);
20932 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20933 style : 'position: absolute',
20934 unselectable : "on",
20937 cls: 'fc-event-inner',
20941 cls: 'fc-event-title',
20949 cls: 'ui-resizable-handle ui-resizable-e',
20950 html : '  '
20956 var ctr = _this.el.select('.fc-event-container',true).first();
20957 var cg = ctr.createChild(cfg);
20959 var sbox = c.select('.fc-day-content',true).first().getBox();
20960 var ebox = c.select('.fc-day-content',true).first().getBox();
20962 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20963 cg.setWidth(ebox.right - sbox.x -2);
20965 cg.on('click', _this.onMoreEventClick, _this, c.more);
20975 onEventEnter: function (e, el,event,d) {
20976 this.fireEvent('evententer', this, el, event);
20979 onEventLeave: function (e, el,event,d) {
20980 this.fireEvent('eventleave', this, el, event);
20983 onEventClick: function (e, el,event,d) {
20984 this.fireEvent('eventclick', this, el, event);
20987 onMonthChange: function () {
20991 onMoreEventClick: function(e, el, more)
20995 this.calpopover.placement = 'right';
20996 this.calpopover.setTitle('More');
20998 this.calpopover.setContent('');
21000 var ctr = this.calpopover.el.select('.popover-content', true).first();
21002 Roo.each(more, function(m){
21004 cls : 'fc-event-hori fc-event-draggable',
21007 var cg = ctr.createChild(cfg);
21009 cg.on('click', _this.onEventClick, _this, m);
21012 this.calpopover.show(el);
21017 onLoad: function ()
21019 this.calevents = [];
21022 if(this.store.getCount() > 0){
21023 this.store.data.each(function(d){
21026 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21027 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21028 time : d.data.start_time,
21029 title : d.data.title,
21030 description : d.data.description,
21031 venue : d.data.venue
21036 this.renderEvents();
21038 if(this.calevents.length && this.loadMask){
21039 this.maskEl.hide();
21043 onBeforeLoad: function()
21045 this.clearEvents();
21047 this.maskEl.show();
21061 * @class Roo.bootstrap.Popover
21062 * @extends Roo.bootstrap.Component
21063 * Bootstrap Popover class
21064 * @cfg {String} html contents of the popover (or false to use children..)
21065 * @cfg {String} title of popover (or false to hide)
21066 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21067 * @cfg {String} trigger click || hover (or false to trigger manually)
21068 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21069 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21070 * - if false and it has a 'parent' then it will be automatically added to that element
21071 * - if string - Roo.get will be called
21072 * @cfg {Number} delay - delay before showing
21075 * Create a new Popover
21076 * @param {Object} config The config object
21079 Roo.bootstrap.Popover = function(config){
21080 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21086 * After the popover show
21088 * @param {Roo.bootstrap.Popover} this
21093 * After the popover hide
21095 * @param {Roo.bootstrap.Popover} this
21101 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21106 placement : 'right',
21107 trigger : 'hover', // hover
21113 can_build_overlaid : false,
21115 maskEl : false, // the mask element
21118 alignEl : false, // when show is called with an element - this get's stored.
21120 getChildContainer : function()
21122 return this.contentEl;
21125 getPopoverHeader : function()
21127 this.title = true; // flag not to hide it..
21128 this.headerEl.addClass('p-0');
21129 return this.headerEl
21133 getAutoCreate : function(){
21136 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21137 style: 'display:block',
21143 cls : 'popover-inner ',
21147 cls: 'popover-title popover-header',
21148 html : this.title === false ? '' : this.title
21151 cls : 'popover-content popover-body ' + (this.cls || ''),
21152 html : this.html || ''
21163 * @param {string} the title
21165 setTitle: function(str)
21169 this.headerEl.dom.innerHTML = str;
21174 * @param {string} the body content
21176 setContent: function(str)
21179 if (this.contentEl) {
21180 this.contentEl.dom.innerHTML = str;
21184 // as it get's added to the bottom of the page.
21185 onRender : function(ct, position)
21187 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21192 var cfg = Roo.apply({}, this.getAutoCreate());
21196 cfg.cls += ' ' + this.cls;
21199 cfg.style = this.style;
21201 //Roo.log("adding to ");
21202 this.el = Roo.get(document.body).createChild(cfg, position);
21203 // Roo.log(this.el);
21206 this.contentEl = this.el.select('.popover-content',true).first();
21207 this.headerEl = this.el.select('.popover-title',true).first();
21210 if(typeof(this.items) != 'undefined'){
21211 var items = this.items;
21214 for(var i =0;i < items.length;i++) {
21215 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21219 this.items = nitems;
21221 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21222 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21229 resizeMask : function()
21231 this.maskEl.setSize(
21232 Roo.lib.Dom.getViewWidth(true),
21233 Roo.lib.Dom.getViewHeight(true)
21237 initEvents : function()
21241 Roo.bootstrap.Popover.register(this);
21244 this.arrowEl = this.el.select('.arrow',true).first();
21245 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21246 this.el.enableDisplayMode('block');
21250 if (this.over === false && !this.parent()) {
21253 if (this.triggers === false) {
21258 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21259 var triggers = this.trigger ? this.trigger.split(' ') : [];
21260 Roo.each(triggers, function(trigger) {
21262 if (trigger == 'click') {
21263 on_el.on('click', this.toggle, this);
21264 } else if (trigger != 'manual') {
21265 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21266 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21268 on_el.on(eventIn ,this.enter, this);
21269 on_el.on(eventOut, this.leave, this);
21279 toggle : function () {
21280 this.hoverState == 'in' ? this.leave() : this.enter();
21283 enter : function () {
21285 clearTimeout(this.timeout);
21287 this.hoverState = 'in';
21289 if (!this.delay || !this.delay.show) {
21294 this.timeout = setTimeout(function () {
21295 if (_t.hoverState == 'in') {
21298 }, this.delay.show)
21301 leave : function() {
21302 clearTimeout(this.timeout);
21304 this.hoverState = 'out';
21306 if (!this.delay || !this.delay.hide) {
21311 this.timeout = setTimeout(function () {
21312 if (_t.hoverState == 'out') {
21315 }, this.delay.hide)
21319 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21320 * @param {string} (left|right|top|bottom) position
21322 show : function (on_el, placement)
21324 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21325 on_el = on_el || false; // default to false
21328 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21329 on_el = this.parent().el;
21330 } else if (this.over) {
21331 on_el = Roo.get(this.over);
21336 this.alignEl = Roo.get( on_el );
21339 this.render(document.body);
21345 if (this.title === false) {
21346 this.headerEl.hide();
21351 this.el.dom.style.display = 'block';
21354 if (this.alignEl) {
21355 this.updatePosition(this.placement, true);
21358 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21359 var es = this.el.getSize();
21360 var x = Roo.lib.Dom.getViewWidth()/2;
21361 var y = Roo.lib.Dom.getViewHeight()/2;
21362 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21367 //var arrow = this.el.select('.arrow',true).first();
21368 //arrow.set(align[2],
21370 this.el.addClass('in');
21374 this.hoverState = 'in';
21377 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21378 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21379 this.maskEl.dom.style.display = 'block';
21380 this.maskEl.addClass('show');
21382 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21384 this.fireEvent('show', this);
21388 * fire this manually after loading a grid in the table for example
21389 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21390 * @param {Boolean} try and move it if we cant get right position.
21392 updatePosition : function(placement, try_move)
21394 // allow for calling with no parameters
21395 placement = placement ? placement : this.placement;
21396 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21398 this.el.removeClass([
21399 'fade','top','bottom', 'left', 'right','in',
21400 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21402 this.el.addClass(placement + ' bs-popover-' + placement);
21404 if (!this.alignEl ) {
21408 switch (placement) {
21410 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21411 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21412 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21413 //normal display... or moved up/down.
21414 this.el.setXY(offset);
21415 var xy = this.alignEl.getAnchorXY('tr', false);
21417 this.arrowEl.setXY(xy);
21420 // continue through...
21421 return this.updatePosition('left', false);
21425 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21426 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21427 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21428 //normal display... or moved up/down.
21429 this.el.setXY(offset);
21430 var xy = this.alignEl.getAnchorXY('tl', false);
21431 xy[0]-=10;xy[1]+=5; // << fix me
21432 this.arrowEl.setXY(xy);
21436 return this.updatePosition('right', false);
21439 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21440 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[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('t', false);
21445 xy[1]-=10; // << fix me
21446 this.arrowEl.setXY(xy);
21450 return this.updatePosition('bottom', false);
21453 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21454 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21455 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21456 //normal display... or moved up/down.
21457 this.el.setXY(offset);
21458 var xy = this.alignEl.getAnchorXY('b', false);
21459 xy[1]+=2; // << fix me
21460 this.arrowEl.setXY(xy);
21464 return this.updatePosition('top', false);
21475 this.el.setXY([0,0]);
21476 this.el.removeClass('in');
21478 this.hoverState = null;
21479 this.maskEl.hide(); // always..
21480 this.fireEvent('hide', this);
21486 Roo.apply(Roo.bootstrap.Popover, {
21489 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21490 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21491 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21492 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21497 clickHander : false,
21501 onMouseDown : function(e)
21503 if (this.popups.length && !e.getTarget(".roo-popover")) {
21504 /// what is nothing is showing..
21513 register : function(popup)
21515 if (!Roo.bootstrap.Popover.clickHandler) {
21516 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21518 // hide other popups.
21519 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21520 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21521 this.hideAll(); //<< why?
21522 //this.popups.push(popup);
21524 hideAll : function()
21526 this.popups.forEach(function(p) {
21530 onShow : function() {
21531 Roo.bootstrap.Popover.popups.push(this);
21533 onHide : function() {
21534 Roo.bootstrap.Popover.popups.remove(this);
21540 * Card header - holder for the card header elements.
21545 * @class Roo.bootstrap.PopoverNav
21546 * @extends Roo.bootstrap.NavGroup
21547 * Bootstrap Popover header navigation class
21549 * Create a new Popover Header Navigation
21550 * @param {Object} config The config object
21553 Roo.bootstrap.PopoverNav = function(config){
21554 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21557 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21560 container_method : 'getPopoverHeader'
21578 * @class Roo.bootstrap.Progress
21579 * @extends Roo.bootstrap.Component
21580 * Bootstrap Progress class
21581 * @cfg {Boolean} striped striped of the progress bar
21582 * @cfg {Boolean} active animated of the progress bar
21586 * Create a new Progress
21587 * @param {Object} config The config object
21590 Roo.bootstrap.Progress = function(config){
21591 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21594 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21599 getAutoCreate : function(){
21607 cfg.cls += ' progress-striped';
21611 cfg.cls += ' active';
21630 * @class Roo.bootstrap.ProgressBar
21631 * @extends Roo.bootstrap.Component
21632 * Bootstrap ProgressBar class
21633 * @cfg {Number} aria_valuenow aria-value now
21634 * @cfg {Number} aria_valuemin aria-value min
21635 * @cfg {Number} aria_valuemax aria-value max
21636 * @cfg {String} label label for the progress bar
21637 * @cfg {String} panel (success | info | warning | danger )
21638 * @cfg {String} role role of the progress bar
21639 * @cfg {String} sr_only text
21643 * Create a new ProgressBar
21644 * @param {Object} config The config object
21647 Roo.bootstrap.ProgressBar = function(config){
21648 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21651 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21655 aria_valuemax : 100,
21661 getAutoCreate : function()
21666 cls: 'progress-bar',
21667 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21679 cfg.role = this.role;
21682 if(this.aria_valuenow){
21683 cfg['aria-valuenow'] = this.aria_valuenow;
21686 if(this.aria_valuemin){
21687 cfg['aria-valuemin'] = this.aria_valuemin;
21690 if(this.aria_valuemax){
21691 cfg['aria-valuemax'] = this.aria_valuemax;
21694 if(this.label && !this.sr_only){
21695 cfg.html = this.label;
21699 cfg.cls += ' progress-bar-' + this.panel;
21705 update : function(aria_valuenow)
21707 this.aria_valuenow = aria_valuenow;
21709 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21724 * @class Roo.bootstrap.TabGroup
21725 * @extends Roo.bootstrap.Column
21726 * Bootstrap Column class
21727 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21728 * @cfg {Boolean} carousel true to make the group behave like a carousel
21729 * @cfg {Boolean} bullets show bullets for the panels
21730 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21731 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21732 * @cfg {Boolean} showarrow (true|false) show arrow default true
21735 * Create a new TabGroup
21736 * @param {Object} config The config object
21739 Roo.bootstrap.TabGroup = function(config){
21740 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21742 this.navId = Roo.id();
21745 Roo.bootstrap.TabGroup.register(this);
21749 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21752 transition : false,
21757 slideOnTouch : false,
21760 getAutoCreate : function()
21762 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21764 cfg.cls += ' tab-content';
21766 if (this.carousel) {
21767 cfg.cls += ' carousel slide';
21770 cls : 'carousel-inner',
21774 if(this.bullets && !Roo.isTouch){
21777 cls : 'carousel-bullets',
21781 if(this.bullets_cls){
21782 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21789 cfg.cn[0].cn.push(bullets);
21792 if(this.showarrow){
21793 cfg.cn[0].cn.push({
21795 class : 'carousel-arrow',
21799 class : 'carousel-prev',
21803 class : 'fa fa-chevron-left'
21809 class : 'carousel-next',
21813 class : 'fa fa-chevron-right'
21826 initEvents: function()
21828 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21829 // this.el.on("touchstart", this.onTouchStart, this);
21832 if(this.autoslide){
21835 this.slideFn = window.setInterval(function() {
21836 _this.showPanelNext();
21840 if(this.showarrow){
21841 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21842 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21848 // onTouchStart : function(e, el, o)
21850 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21854 // this.showPanelNext();
21858 getChildContainer : function()
21860 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21864 * register a Navigation item
21865 * @param {Roo.bootstrap.NavItem} the navitem to add
21867 register : function(item)
21869 this.tabs.push( item);
21870 item.navId = this.navId; // not really needed..
21875 getActivePanel : function()
21878 Roo.each(this.tabs, function(t) {
21888 getPanelByName : function(n)
21891 Roo.each(this.tabs, function(t) {
21892 if (t.tabId == n) {
21900 indexOfPanel : function(p)
21903 Roo.each(this.tabs, function(t,i) {
21904 if (t.tabId == p.tabId) {
21913 * show a specific panel
21914 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21915 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21917 showPanel : function (pan)
21919 if(this.transition || typeof(pan) == 'undefined'){
21920 Roo.log("waiting for the transitionend");
21924 if (typeof(pan) == 'number') {
21925 pan = this.tabs[pan];
21928 if (typeof(pan) == 'string') {
21929 pan = this.getPanelByName(pan);
21932 var cur = this.getActivePanel();
21935 Roo.log('pan or acitve pan is undefined');
21939 if (pan.tabId == this.getActivePanel().tabId) {
21943 if (false === cur.fireEvent('beforedeactivate')) {
21947 if(this.bullets > 0 && !Roo.isTouch){
21948 this.setActiveBullet(this.indexOfPanel(pan));
21951 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21953 //class="carousel-item carousel-item-next carousel-item-left"
21955 this.transition = true;
21956 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21957 var lr = dir == 'next' ? 'left' : 'right';
21958 pan.el.addClass(dir); // or prev
21959 pan.el.addClass('carousel-item-' + dir); // or prev
21960 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21961 cur.el.addClass(lr); // or right
21962 pan.el.addClass(lr);
21963 cur.el.addClass('carousel-item-' +lr); // or right
21964 pan.el.addClass('carousel-item-' +lr);
21968 cur.el.on('transitionend', function() {
21969 Roo.log("trans end?");
21971 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21972 pan.setActive(true);
21974 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21975 cur.setActive(false);
21977 _this.transition = false;
21979 }, this, { single: true } );
21984 cur.setActive(false);
21985 pan.setActive(true);
21990 showPanelNext : function()
21992 var i = this.indexOfPanel(this.getActivePanel());
21994 if (i >= this.tabs.length - 1 && !this.autoslide) {
21998 if (i >= this.tabs.length - 1 && this.autoslide) {
22002 this.showPanel(this.tabs[i+1]);
22005 showPanelPrev : function()
22007 var i = this.indexOfPanel(this.getActivePanel());
22009 if (i < 1 && !this.autoslide) {
22013 if (i < 1 && this.autoslide) {
22014 i = this.tabs.length;
22017 this.showPanel(this.tabs[i-1]);
22021 addBullet: function()
22023 if(!this.bullets || Roo.isTouch){
22026 var ctr = this.el.select('.carousel-bullets',true).first();
22027 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22028 var bullet = ctr.createChild({
22029 cls : 'bullet bullet-' + i
22030 },ctr.dom.lastChild);
22035 bullet.on('click', (function(e, el, o, ii, t){
22037 e.preventDefault();
22039 this.showPanel(ii);
22041 if(this.autoslide && this.slideFn){
22042 clearInterval(this.slideFn);
22043 this.slideFn = window.setInterval(function() {
22044 _this.showPanelNext();
22048 }).createDelegate(this, [i, bullet], true));
22053 setActiveBullet : function(i)
22059 Roo.each(this.el.select('.bullet', true).elements, function(el){
22060 el.removeClass('selected');
22063 var bullet = this.el.select('.bullet-' + i, true).first();
22069 bullet.addClass('selected');
22080 Roo.apply(Roo.bootstrap.TabGroup, {
22084 * register a Navigation Group
22085 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22087 register : function(navgrp)
22089 this.groups[navgrp.navId] = navgrp;
22093 * fetch a Navigation Group based on the navigation ID
22094 * if one does not exist , it will get created.
22095 * @param {string} the navgroup to add
22096 * @returns {Roo.bootstrap.NavGroup} the navgroup
22098 get: function(navId) {
22099 if (typeof(this.groups[navId]) == 'undefined') {
22100 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22102 return this.groups[navId] ;
22117 * @class Roo.bootstrap.TabPanel
22118 * @extends Roo.bootstrap.Component
22119 * Bootstrap TabPanel class
22120 * @cfg {Boolean} active panel active
22121 * @cfg {String} html panel content
22122 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22123 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22124 * @cfg {String} href click to link..
22125 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22129 * Create a new TabPanel
22130 * @param {Object} config The config object
22133 Roo.bootstrap.TabPanel = function(config){
22134 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22138 * Fires when the active status changes
22139 * @param {Roo.bootstrap.TabPanel} this
22140 * @param {Boolean} state the new state
22145 * @event beforedeactivate
22146 * Fires before a tab is de-activated - can be used to do validation on a form.
22147 * @param {Roo.bootstrap.TabPanel} this
22148 * @return {Boolean} false if there is an error
22151 'beforedeactivate': true
22154 this.tabId = this.tabId || Roo.id();
22158 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22165 touchSlide : false,
22166 getAutoCreate : function(){
22171 // item is needed for carousel - not sure if it has any effect otherwise
22172 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22173 html: this.html || ''
22177 cfg.cls += ' active';
22181 cfg.tabId = this.tabId;
22189 initEvents: function()
22191 var p = this.parent();
22193 this.navId = this.navId || p.navId;
22195 if (typeof(this.navId) != 'undefined') {
22196 // not really needed.. but just in case.. parent should be a NavGroup.
22197 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22201 var i = tg.tabs.length - 1;
22203 if(this.active && tg.bullets > 0 && i < tg.bullets){
22204 tg.setActiveBullet(i);
22208 this.el.on('click', this.onClick, this);
22210 if(Roo.isTouch && this.touchSlide){
22211 this.el.on("touchstart", this.onTouchStart, this);
22212 this.el.on("touchmove", this.onTouchMove, this);
22213 this.el.on("touchend", this.onTouchEnd, this);
22218 onRender : function(ct, position)
22220 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22223 setActive : function(state)
22225 Roo.log("panel - set active " + this.tabId + "=" + state);
22227 this.active = state;
22229 this.el.removeClass('active');
22231 } else if (!this.el.hasClass('active')) {
22232 this.el.addClass('active');
22235 this.fireEvent('changed', this, state);
22238 onClick : function(e)
22240 e.preventDefault();
22242 if(!this.href.length){
22246 window.location.href = this.href;
22255 onTouchStart : function(e)
22257 this.swiping = false;
22259 this.startX = e.browserEvent.touches[0].clientX;
22260 this.startY = e.browserEvent.touches[0].clientY;
22263 onTouchMove : function(e)
22265 this.swiping = true;
22267 this.endX = e.browserEvent.touches[0].clientX;
22268 this.endY = e.browserEvent.touches[0].clientY;
22271 onTouchEnd : function(e)
22278 var tabGroup = this.parent();
22280 if(this.endX > this.startX){ // swiping right
22281 tabGroup.showPanelPrev();
22285 if(this.startX > this.endX){ // swiping left
22286 tabGroup.showPanelNext();
22305 * @class Roo.bootstrap.DateField
22306 * @extends Roo.bootstrap.Input
22307 * Bootstrap DateField class
22308 * @cfg {Number} weekStart default 0
22309 * @cfg {String} viewMode default empty, (months|years)
22310 * @cfg {String} minViewMode default empty, (months|years)
22311 * @cfg {Number} startDate default -Infinity
22312 * @cfg {Number} endDate default Infinity
22313 * @cfg {Boolean} todayHighlight default false
22314 * @cfg {Boolean} todayBtn default false
22315 * @cfg {Boolean} calendarWeeks default false
22316 * @cfg {Object} daysOfWeekDisabled default empty
22317 * @cfg {Boolean} singleMode default false (true | false)
22319 * @cfg {Boolean} keyboardNavigation default true
22320 * @cfg {String} language default en
22323 * Create a new DateField
22324 * @param {Object} config The config object
22327 Roo.bootstrap.DateField = function(config){
22328 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22332 * Fires when this field show.
22333 * @param {Roo.bootstrap.DateField} this
22334 * @param {Mixed} date The date value
22339 * Fires when this field hide.
22340 * @param {Roo.bootstrap.DateField} this
22341 * @param {Mixed} date The date value
22346 * Fires when select a date.
22347 * @param {Roo.bootstrap.DateField} this
22348 * @param {Mixed} date The date value
22352 * @event beforeselect
22353 * Fires when before select a date.
22354 * @param {Roo.bootstrap.DateField} this
22355 * @param {Mixed} date The date value
22357 beforeselect : true
22361 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22364 * @cfg {String} format
22365 * The default date format string which can be overriden for localization support. The format must be
22366 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22370 * @cfg {String} altFormats
22371 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22372 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22374 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22382 todayHighlight : false,
22388 keyboardNavigation: true,
22390 calendarWeeks: false,
22392 startDate: -Infinity,
22396 daysOfWeekDisabled: [],
22400 singleMode : false,
22402 UTCDate: function()
22404 return new Date(Date.UTC.apply(Date, arguments));
22407 UTCToday: function()
22409 var today = new Date();
22410 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22413 getDate: function() {
22414 var d = this.getUTCDate();
22415 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22418 getUTCDate: function() {
22422 setDate: function(d) {
22423 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22426 setUTCDate: function(d) {
22428 this.setValue(this.formatDate(this.date));
22431 onRender: function(ct, position)
22434 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22436 this.language = this.language || 'en';
22437 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22438 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22440 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22441 this.format = this.format || 'm/d/y';
22442 this.isInline = false;
22443 this.isInput = true;
22444 this.component = this.el.select('.add-on', true).first() || false;
22445 this.component = (this.component && this.component.length === 0) ? false : this.component;
22446 this.hasInput = this.component && this.inputEl().length;
22448 if (typeof(this.minViewMode === 'string')) {
22449 switch (this.minViewMode) {
22451 this.minViewMode = 1;
22454 this.minViewMode = 2;
22457 this.minViewMode = 0;
22462 if (typeof(this.viewMode === 'string')) {
22463 switch (this.viewMode) {
22476 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22478 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22480 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22482 this.picker().on('mousedown', this.onMousedown, this);
22483 this.picker().on('click', this.onClick, this);
22485 this.picker().addClass('datepicker-dropdown');
22487 this.startViewMode = this.viewMode;
22489 if(this.singleMode){
22490 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22491 v.setVisibilityMode(Roo.Element.DISPLAY);
22495 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22496 v.setStyle('width', '189px');
22500 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22501 if(!this.calendarWeeks){
22506 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22507 v.attr('colspan', function(i, val){
22508 return parseInt(val) + 1;
22513 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22515 this.setStartDate(this.startDate);
22516 this.setEndDate(this.endDate);
22518 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22525 if(this.isInline) {
22530 picker : function()
22532 return this.pickerEl;
22533 // return this.el.select('.datepicker', true).first();
22536 fillDow: function()
22538 var dowCnt = this.weekStart;
22547 if(this.calendarWeeks){
22555 while (dowCnt < this.weekStart + 7) {
22559 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22563 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22566 fillMonths: function()
22569 var months = this.picker().select('>.datepicker-months td', true).first();
22571 months.dom.innerHTML = '';
22577 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22580 months.createChild(month);
22587 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;
22589 if (this.date < this.startDate) {
22590 this.viewDate = new Date(this.startDate);
22591 } else if (this.date > this.endDate) {
22592 this.viewDate = new Date(this.endDate);
22594 this.viewDate = new Date(this.date);
22602 var d = new Date(this.viewDate),
22603 year = d.getUTCFullYear(),
22604 month = d.getUTCMonth(),
22605 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22606 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22607 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22608 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22609 currentDate = this.date && this.date.valueOf(),
22610 today = this.UTCToday();
22612 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22614 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22616 // this.picker.select('>tfoot th.today').
22617 // .text(dates[this.language].today)
22618 // .toggle(this.todayBtn !== false);
22620 this.updateNavArrows();
22623 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22625 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22627 prevMonth.setUTCDate(day);
22629 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22631 var nextMonth = new Date(prevMonth);
22633 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22635 nextMonth = nextMonth.valueOf();
22637 var fillMonths = false;
22639 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22641 while(prevMonth.valueOf() <= nextMonth) {
22644 if (prevMonth.getUTCDay() === this.weekStart) {
22646 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22654 if(this.calendarWeeks){
22655 // ISO 8601: First week contains first thursday.
22656 // ISO also states week starts on Monday, but we can be more abstract here.
22658 // Start of current week: based on weekstart/current date
22659 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22660 // Thursday of this week
22661 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22662 // First Thursday of year, year from thursday
22663 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22664 // Calendar week: ms between thursdays, div ms per day, div 7 days
22665 calWeek = (th - yth) / 864e5 / 7 + 1;
22667 fillMonths.cn.push({
22675 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22677 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22680 if (this.todayHighlight &&
22681 prevMonth.getUTCFullYear() == today.getFullYear() &&
22682 prevMonth.getUTCMonth() == today.getMonth() &&
22683 prevMonth.getUTCDate() == today.getDate()) {
22684 clsName += ' today';
22687 if (currentDate && prevMonth.valueOf() === currentDate) {
22688 clsName += ' active';
22691 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22692 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22693 clsName += ' disabled';
22696 fillMonths.cn.push({
22698 cls: 'day ' + clsName,
22699 html: prevMonth.getDate()
22702 prevMonth.setDate(prevMonth.getDate()+1);
22705 var currentYear = this.date && this.date.getUTCFullYear();
22706 var currentMonth = this.date && this.date.getUTCMonth();
22708 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22710 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22711 v.removeClass('active');
22713 if(currentYear === year && k === currentMonth){
22714 v.addClass('active');
22717 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22718 v.addClass('disabled');
22724 year = parseInt(year/10, 10) * 10;
22726 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22728 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22731 for (var i = -1; i < 11; i++) {
22732 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22734 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22742 showMode: function(dir)
22745 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22748 Roo.each(this.picker().select('>div',true).elements, function(v){
22749 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22752 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22757 if(this.isInline) {
22761 this.picker().removeClass(['bottom', 'top']);
22763 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22765 * place to the top of element!
22769 this.picker().addClass('top');
22770 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22775 this.picker().addClass('bottom');
22777 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22780 parseDate : function(value)
22782 if(!value || value instanceof Date){
22785 var v = Date.parseDate(value, this.format);
22786 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22787 v = Date.parseDate(value, 'Y-m-d');
22789 if(!v && this.altFormats){
22790 if(!this.altFormatsArray){
22791 this.altFormatsArray = this.altFormats.split("|");
22793 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22794 v = Date.parseDate(value, this.altFormatsArray[i]);
22800 formatDate : function(date, fmt)
22802 return (!date || !(date instanceof Date)) ?
22803 date : date.dateFormat(fmt || this.format);
22806 onFocus : function()
22808 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22812 onBlur : function()
22814 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22816 var d = this.inputEl().getValue();
22823 showPopup : function()
22825 this.picker().show();
22829 this.fireEvent('showpopup', this, this.date);
22832 hidePopup : function()
22834 if(this.isInline) {
22837 this.picker().hide();
22838 this.viewMode = this.startViewMode;
22841 this.fireEvent('hidepopup', this, this.date);
22845 onMousedown: function(e)
22847 e.stopPropagation();
22848 e.preventDefault();
22853 Roo.bootstrap.DateField.superclass.keyup.call(this);
22857 setValue: function(v)
22859 if(this.fireEvent('beforeselect', this, v) !== false){
22860 var d = new Date(this.parseDate(v) ).clearTime();
22862 if(isNaN(d.getTime())){
22863 this.date = this.viewDate = '';
22864 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22868 v = this.formatDate(d);
22870 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22872 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22876 this.fireEvent('select', this, this.date);
22880 getValue: function()
22882 return this.formatDate(this.date);
22885 fireKey: function(e)
22887 if (!this.picker().isVisible()){
22888 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22894 var dateChanged = false,
22896 newDate, newViewDate;
22901 e.preventDefault();
22905 if (!this.keyboardNavigation) {
22908 dir = e.keyCode == 37 ? -1 : 1;
22911 newDate = this.moveYear(this.date, dir);
22912 newViewDate = this.moveYear(this.viewDate, dir);
22913 } else if (e.shiftKey){
22914 newDate = this.moveMonth(this.date, dir);
22915 newViewDate = this.moveMonth(this.viewDate, dir);
22917 newDate = new Date(this.date);
22918 newDate.setUTCDate(this.date.getUTCDate() + dir);
22919 newViewDate = new Date(this.viewDate);
22920 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22922 if (this.dateWithinRange(newDate)){
22923 this.date = newDate;
22924 this.viewDate = newViewDate;
22925 this.setValue(this.formatDate(this.date));
22927 e.preventDefault();
22928 dateChanged = true;
22933 if (!this.keyboardNavigation) {
22936 dir = e.keyCode == 38 ? -1 : 1;
22938 newDate = this.moveYear(this.date, dir);
22939 newViewDate = this.moveYear(this.viewDate, dir);
22940 } else if (e.shiftKey){
22941 newDate = this.moveMonth(this.date, dir);
22942 newViewDate = this.moveMonth(this.viewDate, dir);
22944 newDate = new Date(this.date);
22945 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22946 newViewDate = new Date(this.viewDate);
22947 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22949 if (this.dateWithinRange(newDate)){
22950 this.date = newDate;
22951 this.viewDate = newViewDate;
22952 this.setValue(this.formatDate(this.date));
22954 e.preventDefault();
22955 dateChanged = true;
22959 this.setValue(this.formatDate(this.date));
22961 e.preventDefault();
22964 this.setValue(this.formatDate(this.date));
22978 onClick: function(e)
22980 e.stopPropagation();
22981 e.preventDefault();
22983 var target = e.getTarget();
22985 if(target.nodeName.toLowerCase() === 'i'){
22986 target = Roo.get(target).dom.parentNode;
22989 var nodeName = target.nodeName;
22990 var className = target.className;
22991 var html = target.innerHTML;
22992 //Roo.log(nodeName);
22994 switch(nodeName.toLowerCase()) {
22996 switch(className) {
23002 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23003 switch(this.viewMode){
23005 this.viewDate = this.moveMonth(this.viewDate, dir);
23009 this.viewDate = this.moveYear(this.viewDate, dir);
23015 var date = new Date();
23016 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23018 this.setValue(this.formatDate(this.date));
23025 if (className.indexOf('disabled') < 0) {
23026 if (!this.viewDate) {
23027 this.viewDate = new Date();
23029 this.viewDate.setUTCDate(1);
23030 if (className.indexOf('month') > -1) {
23031 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23033 var year = parseInt(html, 10) || 0;
23034 this.viewDate.setUTCFullYear(year);
23038 if(this.singleMode){
23039 this.setValue(this.formatDate(this.viewDate));
23050 //Roo.log(className);
23051 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23052 var day = parseInt(html, 10) || 1;
23053 var year = (this.viewDate || new Date()).getUTCFullYear(),
23054 month = (this.viewDate || new Date()).getUTCMonth();
23056 if (className.indexOf('old') > -1) {
23063 } else if (className.indexOf('new') > -1) {
23071 //Roo.log([year,month,day]);
23072 this.date = this.UTCDate(year, month, day,0,0,0,0);
23073 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23075 //Roo.log(this.formatDate(this.date));
23076 this.setValue(this.formatDate(this.date));
23083 setStartDate: function(startDate)
23085 this.startDate = startDate || -Infinity;
23086 if (this.startDate !== -Infinity) {
23087 this.startDate = this.parseDate(this.startDate);
23090 this.updateNavArrows();
23093 setEndDate: function(endDate)
23095 this.endDate = endDate || Infinity;
23096 if (this.endDate !== Infinity) {
23097 this.endDate = this.parseDate(this.endDate);
23100 this.updateNavArrows();
23103 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23105 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23106 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23107 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23109 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23110 return parseInt(d, 10);
23113 this.updateNavArrows();
23116 updateNavArrows: function()
23118 if(this.singleMode){
23122 var d = new Date(this.viewDate),
23123 year = d.getUTCFullYear(),
23124 month = d.getUTCMonth();
23126 Roo.each(this.picker().select('.prev', true).elements, function(v){
23128 switch (this.viewMode) {
23131 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23137 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23144 Roo.each(this.picker().select('.next', true).elements, function(v){
23146 switch (this.viewMode) {
23149 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23155 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23163 moveMonth: function(date, dir)
23168 var new_date = new Date(date.valueOf()),
23169 day = new_date.getUTCDate(),
23170 month = new_date.getUTCMonth(),
23171 mag = Math.abs(dir),
23173 dir = dir > 0 ? 1 : -1;
23176 // If going back one month, make sure month is not current month
23177 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23179 return new_date.getUTCMonth() == month;
23181 // If going forward one month, make sure month is as expected
23182 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23184 return new_date.getUTCMonth() != new_month;
23186 new_month = month + dir;
23187 new_date.setUTCMonth(new_month);
23188 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23189 if (new_month < 0 || new_month > 11) {
23190 new_month = (new_month + 12) % 12;
23193 // For magnitudes >1, move one month at a time...
23194 for (var i=0; i<mag; i++) {
23195 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23196 new_date = this.moveMonth(new_date, dir);
23198 // ...then reset the day, keeping it in the new month
23199 new_month = new_date.getUTCMonth();
23200 new_date.setUTCDate(day);
23202 return new_month != new_date.getUTCMonth();
23205 // Common date-resetting loop -- if date is beyond end of month, make it
23208 new_date.setUTCDate(--day);
23209 new_date.setUTCMonth(new_month);
23214 moveYear: function(date, dir)
23216 return this.moveMonth(date, dir*12);
23219 dateWithinRange: function(date)
23221 return date >= this.startDate && date <= this.endDate;
23227 this.picker().remove();
23230 validateValue : function(value)
23232 if(this.getVisibilityEl().hasClass('hidden')){
23236 if(value.length < 1) {
23237 if(this.allowBlank){
23243 if(value.length < this.minLength){
23246 if(value.length > this.maxLength){
23250 var vt = Roo.form.VTypes;
23251 if(!vt[this.vtype](value, this)){
23255 if(typeof this.validator == "function"){
23256 var msg = this.validator(value);
23262 if(this.regex && !this.regex.test(value)){
23266 if(typeof(this.parseDate(value)) == 'undefined'){
23270 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23274 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23284 this.date = this.viewDate = '';
23286 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23291 Roo.apply(Roo.bootstrap.DateField, {
23302 html: '<i class="fa fa-arrow-left"/>'
23312 html: '<i class="fa fa-arrow-right"/>'
23354 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23355 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23356 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23357 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23358 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23371 navFnc: 'FullYear',
23376 navFnc: 'FullYear',
23381 Roo.apply(Roo.bootstrap.DateField, {
23385 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23389 cls: 'datepicker-days',
23393 cls: 'table-condensed',
23395 Roo.bootstrap.DateField.head,
23399 Roo.bootstrap.DateField.footer
23406 cls: 'datepicker-months',
23410 cls: 'table-condensed',
23412 Roo.bootstrap.DateField.head,
23413 Roo.bootstrap.DateField.content,
23414 Roo.bootstrap.DateField.footer
23421 cls: 'datepicker-years',
23425 cls: 'table-condensed',
23427 Roo.bootstrap.DateField.head,
23428 Roo.bootstrap.DateField.content,
23429 Roo.bootstrap.DateField.footer
23448 * @class Roo.bootstrap.TimeField
23449 * @extends Roo.bootstrap.Input
23450 * Bootstrap DateField class
23454 * Create a new TimeField
23455 * @param {Object} config The config object
23458 Roo.bootstrap.TimeField = function(config){
23459 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23463 * Fires when this field show.
23464 * @param {Roo.bootstrap.DateField} thisthis
23465 * @param {Mixed} date The date value
23470 * Fires when this field hide.
23471 * @param {Roo.bootstrap.DateField} this
23472 * @param {Mixed} date The date value
23477 * Fires when select a date.
23478 * @param {Roo.bootstrap.DateField} this
23479 * @param {Mixed} date The date value
23485 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23488 * @cfg {String} format
23489 * The default time format string which can be overriden for localization support. The format must be
23490 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23494 getAutoCreate : function()
23496 this.after = '<i class="fa far fa-clock"></i>';
23497 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23501 onRender: function(ct, position)
23504 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23506 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23508 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23510 this.pop = this.picker().select('>.datepicker-time',true).first();
23511 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23513 this.picker().on('mousedown', this.onMousedown, this);
23514 this.picker().on('click', this.onClick, this);
23516 this.picker().addClass('datepicker-dropdown');
23521 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23522 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23523 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23524 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23525 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23526 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23530 fireKey: function(e){
23531 if (!this.picker().isVisible()){
23532 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23538 e.preventDefault();
23546 this.onTogglePeriod();
23549 this.onIncrementMinutes();
23552 this.onDecrementMinutes();
23561 onClick: function(e) {
23562 e.stopPropagation();
23563 e.preventDefault();
23566 picker : function()
23568 return this.pickerEl;
23571 fillTime: function()
23573 var time = this.pop.select('tbody', true).first();
23575 time.dom.innerHTML = '';
23590 cls: 'hours-up fa fas fa-chevron-up'
23610 cls: 'minutes-up fa fas fa-chevron-up'
23631 cls: 'timepicker-hour',
23646 cls: 'timepicker-minute',
23661 cls: 'btn btn-primary period',
23683 cls: 'hours-down fa fas fa-chevron-down'
23703 cls: 'minutes-down fa fas fa-chevron-down'
23721 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23728 var hours = this.time.getHours();
23729 var minutes = this.time.getMinutes();
23742 hours = hours - 12;
23746 hours = '0' + hours;
23750 minutes = '0' + minutes;
23753 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23754 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23755 this.pop.select('button', true).first().dom.innerHTML = period;
23761 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23763 var cls = ['bottom'];
23765 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23772 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23776 //this.picker().setXY(20000,20000);
23777 this.picker().addClass(cls.join('-'));
23781 Roo.each(cls, function(c){
23786 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23787 //_this.picker().setTop(_this.inputEl().getHeight());
23791 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23793 //_this.picker().setTop(0 - _this.picker().getHeight());
23798 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23802 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23810 onFocus : function()
23812 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23816 onBlur : function()
23818 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23824 this.picker().show();
23829 this.fireEvent('show', this, this.date);
23834 this.picker().hide();
23837 this.fireEvent('hide', this, this.date);
23840 setTime : function()
23843 this.setValue(this.time.format(this.format));
23845 this.fireEvent('select', this, this.date);
23850 onMousedown: function(e){
23851 e.stopPropagation();
23852 e.preventDefault();
23855 onIncrementHours: function()
23857 Roo.log('onIncrementHours');
23858 this.time = this.time.add(Date.HOUR, 1);
23863 onDecrementHours: function()
23865 Roo.log('onDecrementHours');
23866 this.time = this.time.add(Date.HOUR, -1);
23870 onIncrementMinutes: function()
23872 Roo.log('onIncrementMinutes');
23873 this.time = this.time.add(Date.MINUTE, 1);
23877 onDecrementMinutes: function()
23879 Roo.log('onDecrementMinutes');
23880 this.time = this.time.add(Date.MINUTE, -1);
23884 onTogglePeriod: function()
23886 Roo.log('onTogglePeriod');
23887 this.time = this.time.add(Date.HOUR, 12);
23895 Roo.apply(Roo.bootstrap.TimeField, {
23899 cls: 'datepicker dropdown-menu',
23903 cls: 'datepicker-time',
23907 cls: 'table-condensed',
23936 cls: 'btn btn-info ok',
23964 * @class Roo.bootstrap.MonthField
23965 * @extends Roo.bootstrap.Input
23966 * Bootstrap MonthField class
23968 * @cfg {String} language default en
23971 * Create a new MonthField
23972 * @param {Object} config The config object
23975 Roo.bootstrap.MonthField = function(config){
23976 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23981 * Fires when this field show.
23982 * @param {Roo.bootstrap.MonthField} this
23983 * @param {Mixed} date The date value
23988 * Fires when this field hide.
23989 * @param {Roo.bootstrap.MonthField} this
23990 * @param {Mixed} date The date value
23995 * Fires when select a date.
23996 * @param {Roo.bootstrap.MonthField} this
23997 * @param {String} oldvalue The old value
23998 * @param {String} newvalue The new value
24004 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24006 onRender: function(ct, position)
24009 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24011 this.language = this.language || 'en';
24012 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24013 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24015 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24016 this.isInline = false;
24017 this.isInput = true;
24018 this.component = this.el.select('.add-on', true).first() || false;
24019 this.component = (this.component && this.component.length === 0) ? false : this.component;
24020 this.hasInput = this.component && this.inputEL().length;
24022 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24024 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24026 this.picker().on('mousedown', this.onMousedown, this);
24027 this.picker().on('click', this.onClick, this);
24029 this.picker().addClass('datepicker-dropdown');
24031 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24032 v.setStyle('width', '189px');
24039 if(this.isInline) {
24045 setValue: function(v, suppressEvent)
24047 var o = this.getValue();
24049 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24053 if(suppressEvent !== true){
24054 this.fireEvent('select', this, o, v);
24059 getValue: function()
24064 onClick: function(e)
24066 e.stopPropagation();
24067 e.preventDefault();
24069 var target = e.getTarget();
24071 if(target.nodeName.toLowerCase() === 'i'){
24072 target = Roo.get(target).dom.parentNode;
24075 var nodeName = target.nodeName;
24076 var className = target.className;
24077 var html = target.innerHTML;
24079 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24083 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24085 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24091 picker : function()
24093 return this.pickerEl;
24096 fillMonths: function()
24099 var months = this.picker().select('>.datepicker-months td', true).first();
24101 months.dom.innerHTML = '';
24107 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24110 months.createChild(month);
24119 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24120 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24123 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24124 e.removeClass('active');
24126 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24127 e.addClass('active');
24134 if(this.isInline) {
24138 this.picker().removeClass(['bottom', 'top']);
24140 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24142 * place to the top of element!
24146 this.picker().addClass('top');
24147 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24152 this.picker().addClass('bottom');
24154 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24157 onFocus : function()
24159 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24163 onBlur : function()
24165 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24167 var d = this.inputEl().getValue();
24176 this.picker().show();
24177 this.picker().select('>.datepicker-months', true).first().show();
24181 this.fireEvent('show', this, this.date);
24186 if(this.isInline) {
24189 this.picker().hide();
24190 this.fireEvent('hide', this, this.date);
24194 onMousedown: function(e)
24196 e.stopPropagation();
24197 e.preventDefault();
24202 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24206 fireKey: function(e)
24208 if (!this.picker().isVisible()){
24209 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24220 e.preventDefault();
24224 dir = e.keyCode == 37 ? -1 : 1;
24226 this.vIndex = this.vIndex + dir;
24228 if(this.vIndex < 0){
24232 if(this.vIndex > 11){
24236 if(isNaN(this.vIndex)){
24240 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24246 dir = e.keyCode == 38 ? -1 : 1;
24248 this.vIndex = this.vIndex + dir * 4;
24250 if(this.vIndex < 0){
24254 if(this.vIndex > 11){
24258 if(isNaN(this.vIndex)){
24262 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24267 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24268 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24272 e.preventDefault();
24275 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24276 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24292 this.picker().remove();
24297 Roo.apply(Roo.bootstrap.MonthField, {
24316 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24317 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24322 Roo.apply(Roo.bootstrap.MonthField, {
24326 cls: 'datepicker dropdown-menu roo-dynamic',
24330 cls: 'datepicker-months',
24334 cls: 'table-condensed',
24336 Roo.bootstrap.DateField.content
24356 * @class Roo.bootstrap.CheckBox
24357 * @extends Roo.bootstrap.Input
24358 * Bootstrap CheckBox class
24360 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24361 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24362 * @cfg {String} boxLabel The text that appears beside the checkbox
24363 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24364 * @cfg {Boolean} checked initnal the element
24365 * @cfg {Boolean} inline inline the element (default false)
24366 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24367 * @cfg {String} tooltip label tooltip
24370 * Create a new CheckBox
24371 * @param {Object} config The config object
24374 Roo.bootstrap.CheckBox = function(config){
24375 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24380 * Fires when the element is checked or unchecked.
24381 * @param {Roo.bootstrap.CheckBox} this This input
24382 * @param {Boolean} checked The new checked value
24387 * Fires when the element is click.
24388 * @param {Roo.bootstrap.CheckBox} this This input
24395 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24397 inputType: 'checkbox',
24406 // checkbox success does not make any sense really..
24411 getAutoCreate : function()
24413 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24419 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24422 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24428 type : this.inputType,
24429 value : this.inputValue,
24430 cls : 'roo-' + this.inputType, //'form-box',
24431 placeholder : this.placeholder || ''
24435 if(this.inputType != 'radio'){
24439 cls : 'roo-hidden-value',
24440 value : this.checked ? this.inputValue : this.valueOff
24445 if (this.weight) { // Validity check?
24446 cfg.cls += " " + this.inputType + "-" + this.weight;
24449 if (this.disabled) {
24450 input.disabled=true;
24454 input.checked = this.checked;
24459 input.name = this.name;
24461 if(this.inputType != 'radio'){
24462 hidden.name = this.name;
24463 input.name = '_hidden_' + this.name;
24468 input.cls += ' input-' + this.size;
24473 ['xs','sm','md','lg'].map(function(size){
24474 if (settings[size]) {
24475 cfg.cls += ' col-' + size + '-' + settings[size];
24479 var inputblock = input;
24481 if (this.before || this.after) {
24484 cls : 'input-group',
24489 inputblock.cn.push({
24491 cls : 'input-group-addon',
24496 inputblock.cn.push(input);
24498 if(this.inputType != 'radio'){
24499 inputblock.cn.push(hidden);
24503 inputblock.cn.push({
24505 cls : 'input-group-addon',
24511 var boxLabelCfg = false;
24517 //'for': id, // box label is handled by onclick - so no for...
24519 html: this.boxLabel
24522 boxLabelCfg.tooltip = this.tooltip;
24528 if (align ==='left' && this.fieldLabel.length) {
24529 // Roo.log("left and has label");
24534 cls : 'control-label',
24535 html : this.fieldLabel
24546 cfg.cn[1].cn.push(boxLabelCfg);
24549 if(this.labelWidth > 12){
24550 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24553 if(this.labelWidth < 13 && this.labelmd == 0){
24554 this.labelmd = this.labelWidth;
24557 if(this.labellg > 0){
24558 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24559 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24562 if(this.labelmd > 0){
24563 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24564 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24567 if(this.labelsm > 0){
24568 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24569 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24572 if(this.labelxs > 0){
24573 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24574 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24577 } else if ( this.fieldLabel.length) {
24578 // Roo.log(" label");
24582 tag: this.boxLabel ? 'span' : 'label',
24584 cls: 'control-label box-input-label',
24585 //cls : 'input-group-addon',
24586 html : this.fieldLabel
24593 cfg.cn.push(boxLabelCfg);
24598 // Roo.log(" no label && no align");
24599 cfg.cn = [ inputblock ] ;
24601 cfg.cn.push(boxLabelCfg);
24609 if(this.inputType != 'radio'){
24610 cfg.cn.push(hidden);
24618 * return the real input element.
24620 inputEl: function ()
24622 return this.el.select('input.roo-' + this.inputType,true).first();
24624 hiddenEl: function ()
24626 return this.el.select('input.roo-hidden-value',true).first();
24629 labelEl: function()
24631 return this.el.select('label.control-label',true).first();
24633 /* depricated... */
24637 return this.labelEl();
24640 boxLabelEl: function()
24642 return this.el.select('label.box-label',true).first();
24645 initEvents : function()
24647 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24649 this.inputEl().on('click', this.onClick, this);
24651 if (this.boxLabel) {
24652 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24655 this.startValue = this.getValue();
24658 Roo.bootstrap.CheckBox.register(this);
24662 onClick : function(e)
24664 if(this.fireEvent('click', this, e) !== false){
24665 this.setChecked(!this.checked);
24670 setChecked : function(state,suppressEvent)
24672 this.startValue = this.getValue();
24674 if(this.inputType == 'radio'){
24676 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24677 e.dom.checked = false;
24680 this.inputEl().dom.checked = true;
24682 this.inputEl().dom.value = this.inputValue;
24684 if(suppressEvent !== true){
24685 this.fireEvent('check', this, true);
24693 this.checked = state;
24695 this.inputEl().dom.checked = state;
24698 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24700 if(suppressEvent !== true){
24701 this.fireEvent('check', this, state);
24707 getValue : function()
24709 if(this.inputType == 'radio'){
24710 return this.getGroupValue();
24713 return this.hiddenEl().dom.value;
24717 getGroupValue : function()
24719 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24723 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24726 setValue : function(v,suppressEvent)
24728 if(this.inputType == 'radio'){
24729 this.setGroupValue(v, suppressEvent);
24733 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24738 setGroupValue : function(v, suppressEvent)
24740 this.startValue = this.getValue();
24742 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24743 e.dom.checked = false;
24745 if(e.dom.value == v){
24746 e.dom.checked = true;
24750 if(suppressEvent !== true){
24751 this.fireEvent('check', this, true);
24759 validate : function()
24761 if(this.getVisibilityEl().hasClass('hidden')){
24767 (this.inputType == 'radio' && this.validateRadio()) ||
24768 (this.inputType == 'checkbox' && this.validateCheckbox())
24774 this.markInvalid();
24778 validateRadio : function()
24780 if(this.getVisibilityEl().hasClass('hidden')){
24784 if(this.allowBlank){
24790 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24791 if(!e.dom.checked){
24803 validateCheckbox : function()
24806 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24807 //return (this.getValue() == this.inputValue) ? true : false;
24810 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24818 for(var i in group){
24819 if(group[i].el.isVisible(true)){
24827 for(var i in group){
24832 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24839 * Mark this field as valid
24841 markValid : function()
24845 this.fireEvent('valid', this);
24847 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24850 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24857 if(this.inputType == 'radio'){
24858 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24859 var fg = e.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');
24873 var fg = this.el.findParent('.form-group', false, true);
24874 if (Roo.bootstrap.version == 3) {
24875 fg.removeClass([this.invalidClass, this.validClass]);
24876 fg.addClass(this.validClass);
24878 fg.removeClass(['is-valid', 'is-invalid']);
24879 fg.addClass('is-valid');
24884 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24890 for(var i in group){
24891 var fg = group[i].el.findParent('.form-group', false, true);
24892 if (Roo.bootstrap.version == 3) {
24893 fg.removeClass([this.invalidClass, this.validClass]);
24894 fg.addClass(this.validClass);
24896 fg.removeClass(['is-valid', 'is-invalid']);
24897 fg.addClass('is-valid');
24903 * Mark this field as invalid
24904 * @param {String} msg The validation message
24906 markInvalid : function(msg)
24908 if(this.allowBlank){
24914 this.fireEvent('invalid', this, msg);
24916 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24919 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24923 label.markInvalid();
24926 if(this.inputType == 'radio'){
24928 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24929 var fg = e.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');
24943 var fg = this.el.findParent('.form-group', false, true);
24944 if (Roo.bootstrap.version == 3) {
24945 fg.removeClass([_this.invalidClass, _this.validClass]);
24946 fg.addClass(_this.invalidClass);
24948 fg.removeClass(['is-invalid', 'is-valid']);
24949 fg.addClass('is-invalid');
24954 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24960 for(var i in group){
24961 var fg = group[i].el.findParent('.form-group', false, true);
24962 if (Roo.bootstrap.version == 3) {
24963 fg.removeClass([_this.invalidClass, _this.validClass]);
24964 fg.addClass(_this.invalidClass);
24966 fg.removeClass(['is-invalid', 'is-valid']);
24967 fg.addClass('is-invalid');
24973 clearInvalid : function()
24975 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24977 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24979 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24981 if (label && label.iconEl) {
24982 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24983 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24987 disable : function()
24989 if(this.inputType != 'radio'){
24990 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24997 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24998 _this.getActionEl().addClass(this.disabledClass);
24999 e.dom.disabled = true;
25003 this.disabled = true;
25004 this.fireEvent("disable", this);
25008 enable : function()
25010 if(this.inputType != 'radio'){
25011 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25018 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25019 _this.getActionEl().removeClass(this.disabledClass);
25020 e.dom.disabled = false;
25024 this.disabled = false;
25025 this.fireEvent("enable", this);
25029 setBoxLabel : function(v)
25034 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25040 Roo.apply(Roo.bootstrap.CheckBox, {
25045 * register a CheckBox Group
25046 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25048 register : function(checkbox)
25050 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25051 this.groups[checkbox.groupId] = {};
25054 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25058 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25062 * fetch a CheckBox Group based on the group ID
25063 * @param {string} the group ID
25064 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25066 get: function(groupId) {
25067 if (typeof(this.groups[groupId]) == 'undefined') {
25071 return this.groups[groupId] ;
25084 * @class Roo.bootstrap.Radio
25085 * @extends Roo.bootstrap.Component
25086 * Bootstrap Radio class
25087 * @cfg {String} boxLabel - the label associated
25088 * @cfg {String} value - the value of radio
25091 * Create a new Radio
25092 * @param {Object} config The config object
25094 Roo.bootstrap.Radio = function(config){
25095 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25099 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25105 getAutoCreate : function()
25109 cls : 'form-group radio',
25114 html : this.boxLabel
25122 initEvents : function()
25124 this.parent().register(this);
25126 this.el.on('click', this.onClick, this);
25130 onClick : function(e)
25132 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25133 this.setChecked(true);
25137 setChecked : function(state, suppressEvent)
25139 this.parent().setValue(this.value, suppressEvent);
25143 setBoxLabel : function(v)
25148 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25163 * @class Roo.bootstrap.SecurePass
25164 * @extends Roo.bootstrap.Input
25165 * Bootstrap SecurePass class
25169 * Create a new SecurePass
25170 * @param {Object} config The config object
25173 Roo.bootstrap.SecurePass = function (config) {
25174 // these go here, so the translation tool can replace them..
25176 PwdEmpty: "Please type a password, and then retype it to confirm.",
25177 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25178 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25179 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25180 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25181 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25182 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25183 TooWeak: "Your password is Too Weak."
25185 this.meterLabel = "Password strength:";
25186 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25187 this.meterClass = [
25188 "roo-password-meter-tooweak",
25189 "roo-password-meter-weak",
25190 "roo-password-meter-medium",
25191 "roo-password-meter-strong",
25192 "roo-password-meter-grey"
25197 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25200 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25202 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25204 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25205 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25206 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25207 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25208 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25209 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25210 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25220 * @cfg {String/Object} Label for the strength meter (defaults to
25221 * 'Password strength:')
25226 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25227 * ['Weak', 'Medium', 'Strong'])
25230 pwdStrengths: false,
25243 initEvents: function ()
25245 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25247 if (this.el.is('input[type=password]') && Roo.isSafari) {
25248 this.el.on('keydown', this.SafariOnKeyDown, this);
25251 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25254 onRender: function (ct, position)
25256 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25257 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25258 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25260 this.trigger.createChild({
25265 cls: 'roo-password-meter-grey col-xs-12',
25268 //width: this.meterWidth + 'px'
25272 cls: 'roo-password-meter-text'
25278 if (this.hideTrigger) {
25279 this.trigger.setDisplayed(false);
25281 this.setSize(this.width || '', this.height || '');
25284 onDestroy: function ()
25286 if (this.trigger) {
25287 this.trigger.removeAllListeners();
25288 this.trigger.remove();
25291 this.wrap.remove();
25293 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25296 checkStrength: function ()
25298 var pwd = this.inputEl().getValue();
25299 if (pwd == this._lastPwd) {
25304 if (this.ClientSideStrongPassword(pwd)) {
25306 } else if (this.ClientSideMediumPassword(pwd)) {
25308 } else if (this.ClientSideWeakPassword(pwd)) {
25314 Roo.log('strength1: ' + strength);
25316 //var pm = this.trigger.child('div/div/div').dom;
25317 var pm = this.trigger.child('div/div');
25318 pm.removeClass(this.meterClass);
25319 pm.addClass(this.meterClass[strength]);
25322 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25324 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25326 this._lastPwd = pwd;
25330 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25332 this._lastPwd = '';
25334 var pm = this.trigger.child('div/div');
25335 pm.removeClass(this.meterClass);
25336 pm.addClass('roo-password-meter-grey');
25339 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25342 this.inputEl().dom.type='password';
25345 validateValue: function (value)
25347 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25350 if (value.length == 0) {
25351 if (this.allowBlank) {
25352 this.clearInvalid();
25356 this.markInvalid(this.errors.PwdEmpty);
25357 this.errorMsg = this.errors.PwdEmpty;
25365 if (!value.match(/[\x21-\x7e]+/)) {
25366 this.markInvalid(this.errors.PwdBadChar);
25367 this.errorMsg = this.errors.PwdBadChar;
25370 if (value.length < 6) {
25371 this.markInvalid(this.errors.PwdShort);
25372 this.errorMsg = this.errors.PwdShort;
25375 if (value.length > 16) {
25376 this.markInvalid(this.errors.PwdLong);
25377 this.errorMsg = this.errors.PwdLong;
25381 if (this.ClientSideStrongPassword(value)) {
25383 } else if (this.ClientSideMediumPassword(value)) {
25385 } else if (this.ClientSideWeakPassword(value)) {
25392 if (strength < 2) {
25393 //this.markInvalid(this.errors.TooWeak);
25394 this.errorMsg = this.errors.TooWeak;
25399 console.log('strength2: ' + strength);
25401 //var pm = this.trigger.child('div/div/div').dom;
25403 var pm = this.trigger.child('div/div');
25404 pm.removeClass(this.meterClass);
25405 pm.addClass(this.meterClass[strength]);
25407 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25409 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25411 this.errorMsg = '';
25415 CharacterSetChecks: function (type)
25418 this.fResult = false;
25421 isctype: function (character, type)
25424 case this.kCapitalLetter:
25425 if (character >= 'A' && character <= 'Z') {
25430 case this.kSmallLetter:
25431 if (character >= 'a' && character <= 'z') {
25437 if (character >= '0' && character <= '9') {
25442 case this.kPunctuation:
25443 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25454 IsLongEnough: function (pwd, size)
25456 return !(pwd == null || isNaN(size) || pwd.length < size);
25459 SpansEnoughCharacterSets: function (word, nb)
25461 if (!this.IsLongEnough(word, nb))
25466 var characterSetChecks = new Array(
25467 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25468 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25471 for (var index = 0; index < word.length; ++index) {
25472 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25473 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25474 characterSetChecks[nCharSet].fResult = true;
25481 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25482 if (characterSetChecks[nCharSet].fResult) {
25487 if (nCharSets < nb) {
25493 ClientSideStrongPassword: function (pwd)
25495 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25498 ClientSideMediumPassword: function (pwd)
25500 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25503 ClientSideWeakPassword: function (pwd)
25505 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25508 })//<script type="text/javascript">
25511 * Based Ext JS Library 1.1.1
25512 * Copyright(c) 2006-2007, Ext JS, LLC.
25518 * @class Roo.HtmlEditorCore
25519 * @extends Roo.Component
25520 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25522 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25525 Roo.HtmlEditorCore = function(config){
25528 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25533 * @event initialize
25534 * Fires when the editor is fully initialized (including the iframe)
25535 * @param {Roo.HtmlEditorCore} this
25540 * Fires when the editor is first receives the focus. Any insertion must wait
25541 * until after this event.
25542 * @param {Roo.HtmlEditorCore} this
25546 * @event beforesync
25547 * Fires before the textarea is updated with content from the editor iframe. Return false
25548 * to cancel the sync.
25549 * @param {Roo.HtmlEditorCore} this
25550 * @param {String} html
25554 * @event beforepush
25555 * Fires before the iframe editor is updated with content from the textarea. Return false
25556 * to cancel the push.
25557 * @param {Roo.HtmlEditorCore} this
25558 * @param {String} html
25563 * Fires when the textarea is updated with content from the editor iframe.
25564 * @param {Roo.HtmlEditorCore} this
25565 * @param {String} html
25570 * Fires when the iframe editor is updated with content from the textarea.
25571 * @param {Roo.HtmlEditorCore} this
25572 * @param {String} html
25577 * @event editorevent
25578 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25579 * @param {Roo.HtmlEditorCore} this
25585 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25587 // defaults : white / black...
25588 this.applyBlacklists();
25595 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25599 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25605 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25610 * @cfg {Number} height (in pixels)
25614 * @cfg {Number} width (in pixels)
25619 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25622 stylesheets: false,
25625 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25627 allowComments: false,
25631 // private properties
25632 validationEvent : false,
25634 initialized : false,
25636 sourceEditMode : false,
25637 onFocus : Roo.emptyFn,
25639 hideMode:'offsets',
25643 // blacklist + whitelisted elements..
25650 * Protected method that will not generally be called directly. It
25651 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25652 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25654 getDocMarkup : function(){
25658 // inherit styels from page...??
25659 if (this.stylesheets === false) {
25661 Roo.get(document.head).select('style').each(function(node) {
25662 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25665 Roo.get(document.head).select('link').each(function(node) {
25666 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25669 } else if (!this.stylesheets.length) {
25671 st = '<style type="text/css">' +
25672 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25675 for (var i in this.stylesheets) {
25676 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25681 st += '<style type="text/css">' +
25682 'IMG { cursor: pointer } ' +
25685 var cls = 'roo-htmleditor-body';
25687 if(this.bodyCls.length){
25688 cls += ' ' + this.bodyCls;
25691 return '<html><head>' + st +
25692 //<style type="text/css">' +
25693 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25695 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25699 onRender : function(ct, position)
25702 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25703 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25706 this.el.dom.style.border = '0 none';
25707 this.el.dom.setAttribute('tabIndex', -1);
25708 this.el.addClass('x-hidden hide');
25712 if(Roo.isIE){ // fix IE 1px bogus margin
25713 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25717 this.frameId = Roo.id();
25721 var iframe = this.owner.wrap.createChild({
25723 cls: 'form-control', // bootstrap..
25725 name: this.frameId,
25726 frameBorder : 'no',
25727 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25732 this.iframe = iframe.dom;
25734 this.assignDocWin();
25736 this.doc.designMode = 'on';
25739 this.doc.write(this.getDocMarkup());
25743 var task = { // must defer to wait for browser to be ready
25745 //console.log("run task?" + this.doc.readyState);
25746 this.assignDocWin();
25747 if(this.doc.body || this.doc.readyState == 'complete'){
25749 this.doc.designMode="on";
25753 Roo.TaskMgr.stop(task);
25754 this.initEditor.defer(10, this);
25761 Roo.TaskMgr.start(task);
25766 onResize : function(w, h)
25768 Roo.log('resize: ' +w + ',' + h );
25769 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25773 if(typeof w == 'number'){
25775 this.iframe.style.width = w + 'px';
25777 if(typeof h == 'number'){
25779 this.iframe.style.height = h + 'px';
25781 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25788 * Toggles the editor between standard and source edit mode.
25789 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25791 toggleSourceEdit : function(sourceEditMode){
25793 this.sourceEditMode = sourceEditMode === true;
25795 if(this.sourceEditMode){
25797 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25800 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25801 //this.iframe.className = '';
25804 //this.setSize(this.owner.wrap.getSize());
25805 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25812 * Protected method that will not generally be called directly. If you need/want
25813 * custom HTML cleanup, this is the method you should override.
25814 * @param {String} html The HTML to be cleaned
25815 * return {String} The cleaned HTML
25817 cleanHtml : function(html){
25818 html = String(html);
25819 if(html.length > 5){
25820 if(Roo.isSafari){ // strip safari nonsense
25821 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25824 if(html == ' '){
25831 * HTML Editor -> Textarea
25832 * Protected method that will not generally be called directly. Syncs the contents
25833 * of the editor iframe with the textarea.
25835 syncValue : function(){
25836 if(this.initialized){
25837 var bd = (this.doc.body || this.doc.documentElement);
25838 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25839 var html = bd.innerHTML;
25841 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25842 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25844 html = '<div style="'+m[0]+'">' + html + '</div>';
25847 html = this.cleanHtml(html);
25848 // fix up the special chars.. normaly like back quotes in word...
25849 // however we do not want to do this with chinese..
25850 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25852 var cc = match.charCodeAt();
25854 // Get the character value, handling surrogate pairs
25855 if (match.length == 2) {
25856 // It's a surrogate pair, calculate the Unicode code point
25857 var high = match.charCodeAt(0) - 0xD800;
25858 var low = match.charCodeAt(1) - 0xDC00;
25859 cc = (high * 0x400) + low + 0x10000;
25861 (cc >= 0x4E00 && cc < 0xA000 ) ||
25862 (cc >= 0x3400 && cc < 0x4E00 ) ||
25863 (cc >= 0xf900 && cc < 0xfb00 )
25868 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25869 return "&#" + cc + ";";
25876 if(this.owner.fireEvent('beforesync', this, html) !== false){
25877 this.el.dom.value = html;
25878 this.owner.fireEvent('sync', this, html);
25884 * Protected method that will not generally be called directly. Pushes the value of the textarea
25885 * into the iframe editor.
25887 pushValue : function(){
25888 if(this.initialized){
25889 var v = this.el.dom.value.trim();
25891 // if(v.length < 1){
25895 if(this.owner.fireEvent('beforepush', this, v) !== false){
25896 var d = (this.doc.body || this.doc.documentElement);
25898 this.cleanUpPaste();
25899 this.el.dom.value = d.innerHTML;
25900 this.owner.fireEvent('push', this, v);
25906 deferFocus : function(){
25907 this.focus.defer(10, this);
25911 focus : function(){
25912 if(this.win && !this.sourceEditMode){
25919 assignDocWin: function()
25921 var iframe = this.iframe;
25924 this.doc = iframe.contentWindow.document;
25925 this.win = iframe.contentWindow;
25927 // if (!Roo.get(this.frameId)) {
25930 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25931 // this.win = Roo.get(this.frameId).dom.contentWindow;
25933 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25937 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25938 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25943 initEditor : function(){
25944 //console.log("INIT EDITOR");
25945 this.assignDocWin();
25949 this.doc.designMode="on";
25951 this.doc.write(this.getDocMarkup());
25954 var dbody = (this.doc.body || this.doc.documentElement);
25955 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25956 // this copies styles from the containing element into thsi one..
25957 // not sure why we need all of this..
25958 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25960 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25961 //ss['background-attachment'] = 'fixed'; // w3c
25962 dbody.bgProperties = 'fixed'; // ie
25963 //Roo.DomHelper.applyStyles(dbody, ss);
25964 Roo.EventManager.on(this.doc, {
25965 //'mousedown': this.onEditorEvent,
25966 'mouseup': this.onEditorEvent,
25967 'dblclick': this.onEditorEvent,
25968 'click': this.onEditorEvent,
25969 'keyup': this.onEditorEvent,
25974 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25976 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25977 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25979 this.initialized = true;
25981 this.owner.fireEvent('initialize', this);
25986 onDestroy : function(){
25992 //for (var i =0; i < this.toolbars.length;i++) {
25993 // // fixme - ask toolbars for heights?
25994 // this.toolbars[i].onDestroy();
25997 //this.wrap.dom.innerHTML = '';
25998 //this.wrap.remove();
26003 onFirstFocus : function(){
26005 this.assignDocWin();
26008 this.activated = true;
26011 if(Roo.isGecko){ // prevent silly gecko errors
26013 var s = this.win.getSelection();
26014 if(!s.focusNode || s.focusNode.nodeType != 3){
26015 var r = s.getRangeAt(0);
26016 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26021 this.execCmd('useCSS', true);
26022 this.execCmd('styleWithCSS', false);
26025 this.owner.fireEvent('activate', this);
26029 adjustFont: function(btn){
26030 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26031 //if(Roo.isSafari){ // safari
26034 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26035 if(Roo.isSafari){ // safari
26036 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26037 v = (v < 10) ? 10 : v;
26038 v = (v > 48) ? 48 : v;
26039 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26044 v = Math.max(1, v+adjust);
26046 this.execCmd('FontSize', v );
26049 onEditorEvent : function(e)
26051 this.owner.fireEvent('editorevent', this, e);
26052 // this.updateToolbar();
26053 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26056 insertTag : function(tg)
26058 // could be a bit smarter... -> wrap the current selected tRoo..
26059 if (tg.toLowerCase() == 'span' ||
26060 tg.toLowerCase() == 'code' ||
26061 tg.toLowerCase() == 'sup' ||
26062 tg.toLowerCase() == 'sub'
26065 range = this.createRange(this.getSelection());
26066 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26067 wrappingNode.appendChild(range.extractContents());
26068 range.insertNode(wrappingNode);
26075 this.execCmd("formatblock", tg);
26079 insertText : function(txt)
26083 var range = this.createRange();
26084 range.deleteContents();
26085 //alert(Sender.getAttribute('label'));
26087 range.insertNode(this.doc.createTextNode(txt));
26093 * Executes a Midas editor command on the editor document and performs necessary focus and
26094 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26095 * @param {String} cmd The Midas command
26096 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26098 relayCmd : function(cmd, value){
26100 this.execCmd(cmd, value);
26101 this.owner.fireEvent('editorevent', this);
26102 //this.updateToolbar();
26103 this.owner.deferFocus();
26107 * Executes a Midas editor command directly on the editor document.
26108 * For visual commands, you should use {@link #relayCmd} instead.
26109 * <b>This should only be called after the editor is initialized.</b>
26110 * @param {String} cmd The Midas command
26111 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26113 execCmd : function(cmd, value){
26114 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26121 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26123 * @param {String} text | dom node..
26125 insertAtCursor : function(text)
26128 if(!this.activated){
26134 var r = this.doc.selection.createRange();
26145 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26149 // from jquery ui (MIT licenced)
26151 var win = this.win;
26153 if (win.getSelection && win.getSelection().getRangeAt) {
26154 range = win.getSelection().getRangeAt(0);
26155 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26156 range.insertNode(node);
26157 } else if (win.document.selection && win.document.selection.createRange) {
26158 // no firefox support
26159 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26160 win.document.selection.createRange().pasteHTML(txt);
26162 // no firefox support
26163 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26164 this.execCmd('InsertHTML', txt);
26173 mozKeyPress : function(e){
26175 var c = e.getCharCode(), cmd;
26178 c = String.fromCharCode(c).toLowerCase();
26192 this.cleanUpPaste.defer(100, this);
26200 e.preventDefault();
26208 fixKeys : function(){ // load time branching for fastest keydown performance
26210 return function(e){
26211 var k = e.getKey(), r;
26214 r = this.doc.selection.createRange();
26217 r.pasteHTML('    ');
26224 r = this.doc.selection.createRange();
26226 var target = r.parentElement();
26227 if(!target || target.tagName.toLowerCase() != 'li'){
26229 r.pasteHTML('<br />');
26235 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26236 this.cleanUpPaste.defer(100, this);
26242 }else if(Roo.isOpera){
26243 return function(e){
26244 var k = e.getKey();
26248 this.execCmd('InsertHTML','    ');
26251 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26252 this.cleanUpPaste.defer(100, this);
26257 }else if(Roo.isSafari){
26258 return function(e){
26259 var k = e.getKey();
26263 this.execCmd('InsertText','\t');
26267 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26268 this.cleanUpPaste.defer(100, this);
26276 getAllAncestors: function()
26278 var p = this.getSelectedNode();
26281 a.push(p); // push blank onto stack..
26282 p = this.getParentElement();
26286 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26290 a.push(this.doc.body);
26294 lastSelNode : false,
26297 getSelection : function()
26299 this.assignDocWin();
26300 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26303 getSelectedNode: function()
26305 // this may only work on Gecko!!!
26307 // should we cache this!!!!
26312 var range = this.createRange(this.getSelection()).cloneRange();
26315 var parent = range.parentElement();
26317 var testRange = range.duplicate();
26318 testRange.moveToElementText(parent);
26319 if (testRange.inRange(range)) {
26322 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26325 parent = parent.parentElement;
26330 // is ancestor a text element.
26331 var ac = range.commonAncestorContainer;
26332 if (ac.nodeType == 3) {
26333 ac = ac.parentNode;
26336 var ar = ac.childNodes;
26339 var other_nodes = [];
26340 var has_other_nodes = false;
26341 for (var i=0;i<ar.length;i++) {
26342 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26345 // fullly contained node.
26347 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26352 // probably selected..
26353 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26354 other_nodes.push(ar[i]);
26358 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26363 has_other_nodes = true;
26365 if (!nodes.length && other_nodes.length) {
26366 nodes= other_nodes;
26368 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26374 createRange: function(sel)
26376 // this has strange effects when using with
26377 // top toolbar - not sure if it's a great idea.
26378 //this.editor.contentWindow.focus();
26379 if (typeof sel != "undefined") {
26381 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26383 return this.doc.createRange();
26386 return this.doc.createRange();
26389 getParentElement: function()
26392 this.assignDocWin();
26393 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26395 var range = this.createRange(sel);
26398 var p = range.commonAncestorContainer;
26399 while (p.nodeType == 3) { // text node
26410 * Range intersection.. the hard stuff...
26414 * [ -- selected range --- ]
26418 * if end is before start or hits it. fail.
26419 * if start is after end or hits it fail.
26421 * if either hits (but other is outside. - then it's not
26427 // @see http://www.thismuchiknow.co.uk/?p=64.
26428 rangeIntersectsNode : function(range, node)
26430 var nodeRange = node.ownerDocument.createRange();
26432 nodeRange.selectNode(node);
26434 nodeRange.selectNodeContents(node);
26437 var rangeStartRange = range.cloneRange();
26438 rangeStartRange.collapse(true);
26440 var rangeEndRange = range.cloneRange();
26441 rangeEndRange.collapse(false);
26443 var nodeStartRange = nodeRange.cloneRange();
26444 nodeStartRange.collapse(true);
26446 var nodeEndRange = nodeRange.cloneRange();
26447 nodeEndRange.collapse(false);
26449 return rangeStartRange.compareBoundaryPoints(
26450 Range.START_TO_START, nodeEndRange) == -1 &&
26451 rangeEndRange.compareBoundaryPoints(
26452 Range.START_TO_START, nodeStartRange) == 1;
26456 rangeCompareNode : function(range, node)
26458 var nodeRange = node.ownerDocument.createRange();
26460 nodeRange.selectNode(node);
26462 nodeRange.selectNodeContents(node);
26466 range.collapse(true);
26468 nodeRange.collapse(true);
26470 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26471 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26473 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26475 var nodeIsBefore = ss == 1;
26476 var nodeIsAfter = ee == -1;
26478 if (nodeIsBefore && nodeIsAfter) {
26481 if (!nodeIsBefore && nodeIsAfter) {
26482 return 1; //right trailed.
26485 if (nodeIsBefore && !nodeIsAfter) {
26486 return 2; // left trailed.
26492 // private? - in a new class?
26493 cleanUpPaste : function()
26495 // cleans up the whole document..
26496 Roo.log('cleanuppaste');
26498 this.cleanUpChildren(this.doc.body);
26499 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26500 if (clean != this.doc.body.innerHTML) {
26501 this.doc.body.innerHTML = clean;
26506 cleanWordChars : function(input) {// change the chars to hex code
26507 var he = Roo.HtmlEditorCore;
26509 var output = input;
26510 Roo.each(he.swapCodes, function(sw) {
26511 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26513 output = output.replace(swapper, sw[1]);
26520 cleanUpChildren : function (n)
26522 if (!n.childNodes.length) {
26525 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26526 this.cleanUpChild(n.childNodes[i]);
26533 cleanUpChild : function (node)
26536 //console.log(node);
26537 if (node.nodeName == "#text") {
26538 // clean up silly Windows -- stuff?
26541 if (node.nodeName == "#comment") {
26542 if (!this.allowComments) {
26543 node.parentNode.removeChild(node);
26545 // clean up silly Windows -- stuff?
26548 var lcname = node.tagName.toLowerCase();
26549 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26550 // whitelist of tags..
26552 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26554 node.parentNode.removeChild(node);
26559 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26561 // spans with no attributes - just remove them..
26562 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26563 remove_keep_children = true;
26566 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26567 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26569 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26570 // remove_keep_children = true;
26573 if (remove_keep_children) {
26574 this.cleanUpChildren(node);
26575 // inserts everything just before this node...
26576 while (node.childNodes.length) {
26577 var cn = node.childNodes[0];
26578 node.removeChild(cn);
26579 node.parentNode.insertBefore(cn, node);
26581 node.parentNode.removeChild(node);
26585 if (!node.attributes || !node.attributes.length) {
26590 this.cleanUpChildren(node);
26594 function cleanAttr(n,v)
26597 if (v.match(/^\./) || v.match(/^\//)) {
26600 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26603 if (v.match(/^#/)) {
26606 if (v.match(/^\{/)) { // allow template editing.
26609 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26610 node.removeAttribute(n);
26614 var cwhite = this.cwhite;
26615 var cblack = this.cblack;
26617 function cleanStyle(n,v)
26619 if (v.match(/expression/)) { //XSS?? should we even bother..
26620 node.removeAttribute(n);
26624 var parts = v.split(/;/);
26627 Roo.each(parts, function(p) {
26628 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26632 var l = p.split(':').shift().replace(/\s+/g,'');
26633 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26635 if ( cwhite.length && cblack.indexOf(l) > -1) {
26636 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26637 //node.removeAttribute(n);
26641 // only allow 'c whitelisted system attributes'
26642 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26643 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26644 //node.removeAttribute(n);
26654 if (clean.length) {
26655 node.setAttribute(n, clean.join(';'));
26657 node.removeAttribute(n);
26663 for (var i = node.attributes.length-1; i > -1 ; i--) {
26664 var a = node.attributes[i];
26667 if (a.name.toLowerCase().substr(0,2)=='on') {
26668 node.removeAttribute(a.name);
26671 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26672 node.removeAttribute(a.name);
26675 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26676 cleanAttr(a.name,a.value); // fixme..
26679 if (a.name == 'style') {
26680 cleanStyle(a.name,a.value);
26683 /// clean up MS crap..
26684 // tecnically this should be a list of valid class'es..
26687 if (a.name == 'class') {
26688 if (a.value.match(/^Mso/)) {
26689 node.removeAttribute('class');
26692 if (a.value.match(/^body$/)) {
26693 node.removeAttribute('class');
26704 this.cleanUpChildren(node);
26710 * Clean up MS wordisms...
26712 cleanWord : function(node)
26715 this.cleanWord(this.doc.body);
26720 node.nodeName == 'SPAN' &&
26721 !node.hasAttributes() &&
26722 node.childNodes.length == 1 &&
26723 node.firstChild.nodeName == "#text"
26725 var textNode = node.firstChild;
26726 node.removeChild(textNode);
26727 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26728 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26730 node.parentNode.insertBefore(textNode, node);
26731 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26732 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26734 node.parentNode.removeChild(node);
26737 if (node.nodeName == "#text") {
26738 // clean up silly Windows -- stuff?
26741 if (node.nodeName == "#comment") {
26742 node.parentNode.removeChild(node);
26743 // clean up silly Windows -- stuff?
26747 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26748 node.parentNode.removeChild(node);
26751 //Roo.log(node.tagName);
26752 // remove - but keep children..
26753 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26754 //Roo.log('-- removed');
26755 while (node.childNodes.length) {
26756 var cn = node.childNodes[0];
26757 node.removeChild(cn);
26758 node.parentNode.insertBefore(cn, node);
26759 // move node to parent - and clean it..
26760 this.cleanWord(cn);
26762 node.parentNode.removeChild(node);
26763 /// no need to iterate chidlren = it's got none..
26764 //this.iterateChildren(node, this.cleanWord);
26768 if (node.className.length) {
26770 var cn = node.className.split(/\W+/);
26772 Roo.each(cn, function(cls) {
26773 if (cls.match(/Mso[a-zA-Z]+/)) {
26778 node.className = cna.length ? cna.join(' ') : '';
26780 node.removeAttribute("class");
26784 if (node.hasAttribute("lang")) {
26785 node.removeAttribute("lang");
26788 if (node.hasAttribute("style")) {
26790 var styles = node.getAttribute("style").split(";");
26792 Roo.each(styles, function(s) {
26793 if (!s.match(/:/)) {
26796 var kv = s.split(":");
26797 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26800 // what ever is left... we allow.
26803 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26804 if (!nstyle.length) {
26805 node.removeAttribute('style');
26808 this.iterateChildren(node, this.cleanWord);
26814 * iterateChildren of a Node, calling fn each time, using this as the scole..
26815 * @param {DomNode} node node to iterate children of.
26816 * @param {Function} fn method of this class to call on each item.
26818 iterateChildren : function(node, fn)
26820 if (!node.childNodes.length) {
26823 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26824 fn.call(this, node.childNodes[i])
26830 * cleanTableWidths.
26832 * Quite often pasting from word etc.. results in tables with column and widths.
26833 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26836 cleanTableWidths : function(node)
26841 this.cleanTableWidths(this.doc.body);
26846 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26849 Roo.log(node.tagName);
26850 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26851 this.iterateChildren(node, this.cleanTableWidths);
26854 if (node.hasAttribute('width')) {
26855 node.removeAttribute('width');
26859 if (node.hasAttribute("style")) {
26862 var styles = node.getAttribute("style").split(";");
26864 Roo.each(styles, function(s) {
26865 if (!s.match(/:/)) {
26868 var kv = s.split(":");
26869 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26872 // what ever is left... we allow.
26875 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26876 if (!nstyle.length) {
26877 node.removeAttribute('style');
26881 this.iterateChildren(node, this.cleanTableWidths);
26889 domToHTML : function(currentElement, depth, nopadtext) {
26891 depth = depth || 0;
26892 nopadtext = nopadtext || false;
26894 if (!currentElement) {
26895 return this.domToHTML(this.doc.body);
26898 //Roo.log(currentElement);
26900 var allText = false;
26901 var nodeName = currentElement.nodeName;
26902 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26904 if (nodeName == '#text') {
26906 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26911 if (nodeName != 'BODY') {
26914 // Prints the node tagName, such as <A>, <IMG>, etc
26917 for(i = 0; i < currentElement.attributes.length;i++) {
26919 var aname = currentElement.attributes.item(i).name;
26920 if (!currentElement.attributes.item(i).value.length) {
26923 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26926 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26935 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26938 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26943 // Traverse the tree
26945 var currentElementChild = currentElement.childNodes.item(i);
26946 var allText = true;
26947 var innerHTML = '';
26949 while (currentElementChild) {
26950 // Formatting code (indent the tree so it looks nice on the screen)
26951 var nopad = nopadtext;
26952 if (lastnode == 'SPAN') {
26956 if (currentElementChild.nodeName == '#text') {
26957 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26958 toadd = nopadtext ? toadd : toadd.trim();
26959 if (!nopad && toadd.length > 80) {
26960 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26962 innerHTML += toadd;
26965 currentElementChild = currentElement.childNodes.item(i);
26971 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26973 // Recursively traverse the tree structure of the child node
26974 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26975 lastnode = currentElementChild.nodeName;
26977 currentElementChild=currentElement.childNodes.item(i);
26983 // The remaining code is mostly for formatting the tree
26984 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26989 ret+= "</"+tagName+">";
26995 applyBlacklists : function()
26997 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26998 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27002 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27003 if (b.indexOf(tag) > -1) {
27006 this.white.push(tag);
27010 Roo.each(w, function(tag) {
27011 if (b.indexOf(tag) > -1) {
27014 if (this.white.indexOf(tag) > -1) {
27017 this.white.push(tag);
27022 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27023 if (w.indexOf(tag) > -1) {
27026 this.black.push(tag);
27030 Roo.each(b, function(tag) {
27031 if (w.indexOf(tag) > -1) {
27034 if (this.black.indexOf(tag) > -1) {
27037 this.black.push(tag);
27042 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27043 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27047 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27048 if (b.indexOf(tag) > -1) {
27051 this.cwhite.push(tag);
27055 Roo.each(w, function(tag) {
27056 if (b.indexOf(tag) > -1) {
27059 if (this.cwhite.indexOf(tag) > -1) {
27062 this.cwhite.push(tag);
27067 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27068 if (w.indexOf(tag) > -1) {
27071 this.cblack.push(tag);
27075 Roo.each(b, function(tag) {
27076 if (w.indexOf(tag) > -1) {
27079 if (this.cblack.indexOf(tag) > -1) {
27082 this.cblack.push(tag);
27087 setStylesheets : function(stylesheets)
27089 if(typeof(stylesheets) == 'string'){
27090 Roo.get(this.iframe.contentDocument.head).createChild({
27092 rel : 'stylesheet',
27101 Roo.each(stylesheets, function(s) {
27106 Roo.get(_this.iframe.contentDocument.head).createChild({
27108 rel : 'stylesheet',
27117 removeStylesheets : function()
27121 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27126 setStyle : function(style)
27128 Roo.get(this.iframe.contentDocument.head).createChild({
27137 // hide stuff that is not compatible
27151 * @event specialkey
27155 * @cfg {String} fieldClass @hide
27158 * @cfg {String} focusClass @hide
27161 * @cfg {String} autoCreate @hide
27164 * @cfg {String} inputType @hide
27167 * @cfg {String} invalidClass @hide
27170 * @cfg {String} invalidText @hide
27173 * @cfg {String} msgFx @hide
27176 * @cfg {String} validateOnBlur @hide
27180 Roo.HtmlEditorCore.white = [
27181 'area', 'br', 'img', 'input', 'hr', 'wbr',
27183 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27184 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27185 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27186 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27187 'table', 'ul', 'xmp',
27189 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27192 'dir', 'menu', 'ol', 'ul', 'dl',
27198 Roo.HtmlEditorCore.black = [
27199 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27201 'base', 'basefont', 'bgsound', 'blink', 'body',
27202 'frame', 'frameset', 'head', 'html', 'ilayer',
27203 'iframe', 'layer', 'link', 'meta', 'object',
27204 'script', 'style' ,'title', 'xml' // clean later..
27206 Roo.HtmlEditorCore.clean = [
27207 'script', 'style', 'title', 'xml'
27209 Roo.HtmlEditorCore.remove = [
27214 Roo.HtmlEditorCore.ablack = [
27218 Roo.HtmlEditorCore.aclean = [
27219 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27223 Roo.HtmlEditorCore.pwhite= [
27224 'http', 'https', 'mailto'
27227 // white listed style attributes.
27228 Roo.HtmlEditorCore.cwhite= [
27229 // 'text-align', /// default is to allow most things..
27235 // black listed style attributes.
27236 Roo.HtmlEditorCore.cblack= [
27237 // 'font-size' -- this can be set by the project
27241 Roo.HtmlEditorCore.swapCodes =[
27242 [ 8211, "–" ],
27243 [ 8212, "—" ],
27260 * @class Roo.bootstrap.HtmlEditor
27261 * @extends Roo.bootstrap.TextArea
27262 * Bootstrap HtmlEditor class
27265 * Create a new HtmlEditor
27266 * @param {Object} config The config object
27269 Roo.bootstrap.HtmlEditor = function(config){
27270 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27271 if (!this.toolbars) {
27272 this.toolbars = [];
27275 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27278 * @event initialize
27279 * Fires when the editor is fully initialized (including the iframe)
27280 * @param {HtmlEditor} this
27285 * Fires when the editor is first receives the focus. Any insertion must wait
27286 * until after this event.
27287 * @param {HtmlEditor} this
27291 * @event beforesync
27292 * Fires before the textarea is updated with content from the editor iframe. Return false
27293 * to cancel the sync.
27294 * @param {HtmlEditor} this
27295 * @param {String} html
27299 * @event beforepush
27300 * Fires before the iframe editor is updated with content from the textarea. Return false
27301 * to cancel the push.
27302 * @param {HtmlEditor} this
27303 * @param {String} html
27308 * Fires when the textarea is updated with content from the editor iframe.
27309 * @param {HtmlEditor} this
27310 * @param {String} html
27315 * Fires when the iframe editor is updated with content from the textarea.
27316 * @param {HtmlEditor} this
27317 * @param {String} html
27321 * @event editmodechange
27322 * Fires when the editor switches edit modes
27323 * @param {HtmlEditor} this
27324 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27326 editmodechange: true,
27328 * @event editorevent
27329 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27330 * @param {HtmlEditor} this
27334 * @event firstfocus
27335 * Fires when on first focus - needed by toolbars..
27336 * @param {HtmlEditor} this
27341 * Auto save the htmlEditor value as a file into Events
27342 * @param {HtmlEditor} this
27346 * @event savedpreview
27347 * preview the saved version of htmlEditor
27348 * @param {HtmlEditor} this
27355 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27359 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27364 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27369 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27374 * @cfg {Number} height (in pixels)
27378 * @cfg {Number} width (in pixels)
27383 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27386 stylesheets: false,
27391 // private properties
27392 validationEvent : false,
27394 initialized : false,
27397 onFocus : Roo.emptyFn,
27399 hideMode:'offsets',
27401 tbContainer : false,
27405 toolbarContainer :function() {
27406 return this.wrap.select('.x-html-editor-tb',true).first();
27410 * Protected method that will not generally be called directly. It
27411 * is called when the editor creates its toolbar. Override this method if you need to
27412 * add custom toolbar buttons.
27413 * @param {HtmlEditor} editor
27415 createToolbar : function(){
27416 Roo.log('renewing');
27417 Roo.log("create toolbars");
27419 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27420 this.toolbars[0].render(this.toolbarContainer());
27424 // if (!editor.toolbars || !editor.toolbars.length) {
27425 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27428 // for (var i =0 ; i < editor.toolbars.length;i++) {
27429 // editor.toolbars[i] = Roo.factory(
27430 // typeof(editor.toolbars[i]) == 'string' ?
27431 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27432 // Roo.bootstrap.HtmlEditor);
27433 // editor.toolbars[i].init(editor);
27439 onRender : function(ct, position)
27441 // Roo.log("Call onRender: " + this.xtype);
27443 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27445 this.wrap = this.inputEl().wrap({
27446 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27449 this.editorcore.onRender(ct, position);
27451 if (this.resizable) {
27452 this.resizeEl = new Roo.Resizable(this.wrap, {
27456 minHeight : this.height,
27457 height: this.height,
27458 handles : this.resizable,
27461 resize : function(r, w, h) {
27462 _t.onResize(w,h); // -something
27468 this.createToolbar(this);
27471 if(!this.width && this.resizable){
27472 this.setSize(this.wrap.getSize());
27474 if (this.resizeEl) {
27475 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27476 // should trigger onReize..
27482 onResize : function(w, h)
27484 Roo.log('resize: ' +w + ',' + h );
27485 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27489 if(this.inputEl() ){
27490 if(typeof w == 'number'){
27491 var aw = w - this.wrap.getFrameWidth('lr');
27492 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27495 if(typeof h == 'number'){
27496 var tbh = -11; // fixme it needs to tool bar size!
27497 for (var i =0; i < this.toolbars.length;i++) {
27498 // fixme - ask toolbars for heights?
27499 tbh += this.toolbars[i].el.getHeight();
27500 //if (this.toolbars[i].footer) {
27501 // tbh += this.toolbars[i].footer.el.getHeight();
27509 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27510 ah -= 5; // knock a few pixes off for look..
27511 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27515 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27516 this.editorcore.onResize(ew,eh);
27521 * Toggles the editor between standard and source edit mode.
27522 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27524 toggleSourceEdit : function(sourceEditMode)
27526 this.editorcore.toggleSourceEdit(sourceEditMode);
27528 if(this.editorcore.sourceEditMode){
27529 Roo.log('editor - showing textarea');
27532 // Roo.log(this.syncValue());
27534 this.inputEl().removeClass(['hide', 'x-hidden']);
27535 this.inputEl().dom.removeAttribute('tabIndex');
27536 this.inputEl().focus();
27538 Roo.log('editor - hiding textarea');
27540 // Roo.log(this.pushValue());
27543 this.inputEl().addClass(['hide', 'x-hidden']);
27544 this.inputEl().dom.setAttribute('tabIndex', -1);
27545 //this.deferFocus();
27548 if(this.resizable){
27549 this.setSize(this.wrap.getSize());
27552 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27555 // private (for BoxComponent)
27556 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27558 // private (for BoxComponent)
27559 getResizeEl : function(){
27563 // private (for BoxComponent)
27564 getPositionEl : function(){
27569 initEvents : function(){
27570 this.originalValue = this.getValue();
27574 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27577 // markInvalid : Roo.emptyFn,
27579 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27582 // clearInvalid : Roo.emptyFn,
27584 setValue : function(v){
27585 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27586 this.editorcore.pushValue();
27591 deferFocus : function(){
27592 this.focus.defer(10, this);
27596 focus : function(){
27597 this.editorcore.focus();
27603 onDestroy : function(){
27609 for (var i =0; i < this.toolbars.length;i++) {
27610 // fixme - ask toolbars for heights?
27611 this.toolbars[i].onDestroy();
27614 this.wrap.dom.innerHTML = '';
27615 this.wrap.remove();
27620 onFirstFocus : function(){
27621 //Roo.log("onFirstFocus");
27622 this.editorcore.onFirstFocus();
27623 for (var i =0; i < this.toolbars.length;i++) {
27624 this.toolbars[i].onFirstFocus();
27630 syncValue : function()
27632 this.editorcore.syncValue();
27635 pushValue : function()
27637 this.editorcore.pushValue();
27641 // hide stuff that is not compatible
27655 * @event specialkey
27659 * @cfg {String} fieldClass @hide
27662 * @cfg {String} focusClass @hide
27665 * @cfg {String} autoCreate @hide
27668 * @cfg {String} inputType @hide
27672 * @cfg {String} invalidText @hide
27675 * @cfg {String} msgFx @hide
27678 * @cfg {String} validateOnBlur @hide
27687 Roo.namespace('Roo.bootstrap.htmleditor');
27689 * @class Roo.bootstrap.HtmlEditorToolbar1
27695 new Roo.bootstrap.HtmlEditor({
27698 new Roo.bootstrap.HtmlEditorToolbar1({
27699 disable : { fonts: 1 , format: 1, ..., ... , ...],
27705 * @cfg {Object} disable List of elements to disable..
27706 * @cfg {Array} btns List of additional buttons.
27710 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27713 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27716 Roo.apply(this, config);
27718 // default disabled, based on 'good practice'..
27719 this.disable = this.disable || {};
27720 Roo.applyIf(this.disable, {
27723 specialElements : true
27725 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27727 this.editor = config.editor;
27728 this.editorcore = config.editor.editorcore;
27730 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27732 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27733 // dont call parent... till later.
27735 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27740 editorcore : false,
27745 "h1","h2","h3","h4","h5","h6",
27747 "abbr", "acronym", "address", "cite", "samp", "var",
27751 onRender : function(ct, position)
27753 // Roo.log("Call onRender: " + this.xtype);
27755 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27757 this.el.dom.style.marginBottom = '0';
27759 var editorcore = this.editorcore;
27760 var editor= this.editor;
27763 var btn = function(id,cmd , toggle, handler, html){
27765 var event = toggle ? 'toggle' : 'click';
27770 xns: Roo.bootstrap,
27774 enableToggle:toggle !== false,
27776 pressed : toggle ? false : null,
27779 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27780 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27786 // var cb_box = function...
27791 xns: Roo.bootstrap,
27796 xns: Roo.bootstrap,
27800 Roo.each(this.formats, function(f) {
27801 style.menu.items.push({
27803 xns: Roo.bootstrap,
27804 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27809 editorcore.insertTag(this.tagname);
27816 children.push(style);
27818 btn('bold',false,true);
27819 btn('italic',false,true);
27820 btn('align-left', 'justifyleft',true);
27821 btn('align-center', 'justifycenter',true);
27822 btn('align-right' , 'justifyright',true);
27823 btn('link', false, false, function(btn) {
27824 //Roo.log("create link?");
27825 var url = prompt(this.createLinkText, this.defaultLinkValue);
27826 if(url && url != 'http:/'+'/'){
27827 this.editorcore.relayCmd('createlink', url);
27830 btn('list','insertunorderedlist',true);
27831 btn('pencil', false,true, function(btn){
27833 this.toggleSourceEdit(btn.pressed);
27836 if (this.editor.btns.length > 0) {
27837 for (var i = 0; i<this.editor.btns.length; i++) {
27838 children.push(this.editor.btns[i]);
27846 xns: Roo.bootstrap,
27851 xns: Roo.bootstrap,
27856 cog.menu.items.push({
27858 xns: Roo.bootstrap,
27859 html : Clean styles,
27864 editorcore.insertTag(this.tagname);
27873 this.xtype = 'NavSimplebar';
27875 for(var i=0;i< children.length;i++) {
27877 this.buttons.add(this.addxtypeChild(children[i]));
27881 editor.on('editorevent', this.updateToolbar, this);
27883 onBtnClick : function(id)
27885 this.editorcore.relayCmd(id);
27886 this.editorcore.focus();
27890 * Protected method that will not generally be called directly. It triggers
27891 * a toolbar update by reading the markup state of the current selection in the editor.
27893 updateToolbar: function(){
27895 if(!this.editorcore.activated){
27896 this.editor.onFirstFocus(); // is this neeed?
27900 var btns = this.buttons;
27901 var doc = this.editorcore.doc;
27902 btns.get('bold').setActive(doc.queryCommandState('bold'));
27903 btns.get('italic').setActive(doc.queryCommandState('italic'));
27904 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27906 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27907 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27908 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27910 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27911 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27914 var ans = this.editorcore.getAllAncestors();
27915 if (this.formatCombo) {
27918 var store = this.formatCombo.store;
27919 this.formatCombo.setValue("");
27920 for (var i =0; i < ans.length;i++) {
27921 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27923 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27931 // hides menus... - so this cant be on a menu...
27932 Roo.bootstrap.MenuMgr.hideAll();
27934 Roo.bootstrap.MenuMgr.hideAll();
27935 //this.editorsyncValue();
27937 onFirstFocus: function() {
27938 this.buttons.each(function(item){
27942 toggleSourceEdit : function(sourceEditMode){
27945 if(sourceEditMode){
27946 Roo.log("disabling buttons");
27947 this.buttons.each( function(item){
27948 if(item.cmd != 'pencil'){
27954 Roo.log("enabling buttons");
27955 if(this.editorcore.initialized){
27956 this.buttons.each( function(item){
27962 Roo.log("calling toggole on editor");
27963 // tell the editor that it's been pressed..
27964 this.editor.toggleSourceEdit(sourceEditMode);
27978 * @class Roo.bootstrap.Markdown
27979 * @extends Roo.bootstrap.TextArea
27980 * Bootstrap Showdown editable area
27981 * @cfg {string} content
27984 * Create a new Showdown
27987 Roo.bootstrap.Markdown = function(config){
27988 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27992 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27996 initEvents : function()
27999 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28000 this.markdownEl = this.el.createChild({
28001 cls : 'roo-markdown-area'
28003 this.inputEl().addClass('d-none');
28004 if (this.getValue() == '') {
28005 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28008 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28010 this.markdownEl.on('click', this.toggleTextEdit, this);
28011 this.on('blur', this.toggleTextEdit, this);
28012 this.on('specialkey', this.resizeTextArea, this);
28015 toggleTextEdit : function()
28017 var sh = this.markdownEl.getHeight();
28018 this.inputEl().addClass('d-none');
28019 this.markdownEl.addClass('d-none');
28020 if (!this.editing) {
28022 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28023 this.inputEl().removeClass('d-none');
28024 this.inputEl().focus();
28025 this.editing = true;
28028 // show showdown...
28029 this.updateMarkdown();
28030 this.markdownEl.removeClass('d-none');
28031 this.editing = false;
28034 updateMarkdown : function()
28036 if (this.getValue() == '') {
28037 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28041 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28044 resizeTextArea: function () {
28047 Roo.log([sh, this.getValue().split("\n").length * 30]);
28048 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28050 setValue : function(val)
28052 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28053 if (!this.editing) {
28054 this.updateMarkdown();
28060 if (!this.editing) {
28061 this.toggleTextEdit();
28069 * Ext JS Library 1.1.1
28070 * Copyright(c) 2006-2007, Ext JS, LLC.
28072 * Originally Released Under LGPL - original licence link has changed is not relivant.
28075 * <script type="text/javascript">
28079 * @class Roo.bootstrap.PagingToolbar
28080 * @extends Roo.bootstrap.NavSimplebar
28081 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28083 * Create a new PagingToolbar
28084 * @param {Object} config The config object
28085 * @param {Roo.data.Store} store
28087 Roo.bootstrap.PagingToolbar = function(config)
28089 // old args format still supported... - xtype is prefered..
28090 // created from xtype...
28092 this.ds = config.dataSource;
28094 if (config.store && !this.ds) {
28095 this.store= Roo.factory(config.store, Roo.data);
28096 this.ds = this.store;
28097 this.ds.xmodule = this.xmodule || false;
28100 this.toolbarItems = [];
28101 if (config.items) {
28102 this.toolbarItems = config.items;
28105 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28110 this.bind(this.ds);
28113 if (Roo.bootstrap.version == 4) {
28114 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28116 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28121 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28123 * @cfg {Roo.data.Store} dataSource
28124 * The underlying data store providing the paged data
28127 * @cfg {String/HTMLElement/Element} container
28128 * container The id or element that will contain the toolbar
28131 * @cfg {Boolean} displayInfo
28132 * True to display the displayMsg (defaults to false)
28135 * @cfg {Number} pageSize
28136 * The number of records to display per page (defaults to 20)
28140 * @cfg {String} displayMsg
28141 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28143 displayMsg : 'Displaying {0} - {1} of {2}',
28145 * @cfg {String} emptyMsg
28146 * The message to display when no records are found (defaults to "No data to display")
28148 emptyMsg : 'No data to display',
28150 * Customizable piece of the default paging text (defaults to "Page")
28153 beforePageText : "Page",
28155 * Customizable piece of the default paging text (defaults to "of %0")
28158 afterPageText : "of {0}",
28160 * Customizable piece of the default paging text (defaults to "First Page")
28163 firstText : "First Page",
28165 * Customizable piece of the default paging text (defaults to "Previous Page")
28168 prevText : "Previous Page",
28170 * Customizable piece of the default paging text (defaults to "Next Page")
28173 nextText : "Next Page",
28175 * Customizable piece of the default paging text (defaults to "Last Page")
28178 lastText : "Last Page",
28180 * Customizable piece of the default paging text (defaults to "Refresh")
28183 refreshText : "Refresh",
28187 onRender : function(ct, position)
28189 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28190 this.navgroup.parentId = this.id;
28191 this.navgroup.onRender(this.el, null);
28192 // add the buttons to the navgroup
28194 if(this.displayInfo){
28195 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28196 this.displayEl = this.el.select('.x-paging-info', true).first();
28197 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28198 // this.displayEl = navel.el.select('span',true).first();
28204 Roo.each(_this.buttons, function(e){ // this might need to use render????
28205 Roo.factory(e).render(_this.el);
28209 Roo.each(_this.toolbarItems, function(e) {
28210 _this.navgroup.addItem(e);
28214 this.first = this.navgroup.addItem({
28215 tooltip: this.firstText,
28216 cls: "prev btn-outline-secondary",
28217 html : ' <i class="fa fa-step-backward"></i>',
28219 preventDefault: true,
28220 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28223 this.prev = this.navgroup.addItem({
28224 tooltip: this.prevText,
28225 cls: "prev btn-outline-secondary",
28226 html : ' <i class="fa fa-backward"></i>',
28228 preventDefault: true,
28229 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28231 //this.addSeparator();
28234 var field = this.navgroup.addItem( {
28236 cls : 'x-paging-position btn-outline-secondary',
28238 html : this.beforePageText +
28239 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28240 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28243 this.field = field.el.select('input', true).first();
28244 this.field.on("keydown", this.onPagingKeydown, this);
28245 this.field.on("focus", function(){this.dom.select();});
28248 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28249 //this.field.setHeight(18);
28250 //this.addSeparator();
28251 this.next = this.navgroup.addItem({
28252 tooltip: this.nextText,
28253 cls: "next btn-outline-secondary",
28254 html : ' <i class="fa fa-forward"></i>',
28256 preventDefault: true,
28257 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28259 this.last = this.navgroup.addItem({
28260 tooltip: this.lastText,
28261 html : ' <i class="fa fa-step-forward"></i>',
28262 cls: "next btn-outline-secondary",
28264 preventDefault: true,
28265 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28267 //this.addSeparator();
28268 this.loading = this.navgroup.addItem({
28269 tooltip: this.refreshText,
28270 cls: "btn-outline-secondary",
28271 html : ' <i class="fa fa-refresh"></i>',
28272 preventDefault: true,
28273 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28279 updateInfo : function(){
28280 if(this.displayEl){
28281 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28282 var msg = count == 0 ?
28286 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28288 this.displayEl.update(msg);
28293 onLoad : function(ds, r, o)
28295 this.cursor = o.params && o.params.start ? o.params.start : 0;
28297 var d = this.getPageData(),
28302 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28303 this.field.dom.value = ap;
28304 this.first.setDisabled(ap == 1);
28305 this.prev.setDisabled(ap == 1);
28306 this.next.setDisabled(ap == ps);
28307 this.last.setDisabled(ap == ps);
28308 this.loading.enable();
28313 getPageData : function(){
28314 var total = this.ds.getTotalCount();
28317 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28318 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28323 onLoadError : function(){
28324 this.loading.enable();
28328 onPagingKeydown : function(e){
28329 var k = e.getKey();
28330 var d = this.getPageData();
28332 var v = this.field.dom.value, pageNum;
28333 if(!v || isNaN(pageNum = parseInt(v, 10))){
28334 this.field.dom.value = d.activePage;
28337 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28338 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28341 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))
28343 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28344 this.field.dom.value = pageNum;
28345 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28348 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28350 var v = this.field.dom.value, pageNum;
28351 var increment = (e.shiftKey) ? 10 : 1;
28352 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28355 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28356 this.field.dom.value = d.activePage;
28359 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28361 this.field.dom.value = parseInt(v, 10) + increment;
28362 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28363 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28370 beforeLoad : function(){
28372 this.loading.disable();
28377 onClick : function(which){
28386 ds.load({params:{start: 0, limit: this.pageSize}});
28389 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28392 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28395 var total = ds.getTotalCount();
28396 var extra = total % this.pageSize;
28397 var lastStart = extra ? (total - extra) : total-this.pageSize;
28398 ds.load({params:{start: lastStart, limit: this.pageSize}});
28401 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28407 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28408 * @param {Roo.data.Store} store The data store to unbind
28410 unbind : function(ds){
28411 ds.un("beforeload", this.beforeLoad, this);
28412 ds.un("load", this.onLoad, this);
28413 ds.un("loadexception", this.onLoadError, this);
28414 ds.un("remove", this.updateInfo, this);
28415 ds.un("add", this.updateInfo, this);
28416 this.ds = undefined;
28420 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28421 * @param {Roo.data.Store} store The data store to bind
28423 bind : function(ds){
28424 ds.on("beforeload", this.beforeLoad, this);
28425 ds.on("load", this.onLoad, this);
28426 ds.on("loadexception", this.onLoadError, this);
28427 ds.on("remove", this.updateInfo, this);
28428 ds.on("add", this.updateInfo, this);
28439 * @class Roo.bootstrap.MessageBar
28440 * @extends Roo.bootstrap.Component
28441 * Bootstrap MessageBar class
28442 * @cfg {String} html contents of the MessageBar
28443 * @cfg {String} weight (info | success | warning | danger) default info
28444 * @cfg {String} beforeClass insert the bar before the given class
28445 * @cfg {Boolean} closable (true | false) default false
28446 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28449 * Create a new Element
28450 * @param {Object} config The config object
28453 Roo.bootstrap.MessageBar = function(config){
28454 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28457 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28463 beforeClass: 'bootstrap-sticky-wrap',
28465 getAutoCreate : function(){
28469 cls: 'alert alert-dismissable alert-' + this.weight,
28474 html: this.html || ''
28480 cfg.cls += ' alert-messages-fixed';
28494 onRender : function(ct, position)
28496 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28499 var cfg = Roo.apply({}, this.getAutoCreate());
28503 cfg.cls += ' ' + this.cls;
28506 cfg.style = this.style;
28508 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28510 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28513 this.el.select('>button.close').on('click', this.hide, this);
28519 if (!this.rendered) {
28525 this.fireEvent('show', this);
28531 if (!this.rendered) {
28537 this.fireEvent('hide', this);
28540 update : function()
28542 // var e = this.el.dom.firstChild;
28544 // if(this.closable){
28545 // e = e.nextSibling;
28548 // e.data = this.html || '';
28550 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28566 * @class Roo.bootstrap.Graph
28567 * @extends Roo.bootstrap.Component
28568 * Bootstrap Graph class
28572 @cfg {String} graphtype bar | vbar | pie
28573 @cfg {number} g_x coodinator | centre x (pie)
28574 @cfg {number} g_y coodinator | centre y (pie)
28575 @cfg {number} g_r radius (pie)
28576 @cfg {number} g_height height of the chart (respected by all elements in the set)
28577 @cfg {number} g_width width of the chart (respected by all elements in the set)
28578 @cfg {Object} title The title of the chart
28581 -opts (object) options for the chart
28583 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28584 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28586 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.
28587 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28589 o stretch (boolean)
28591 -opts (object) options for the pie
28594 o startAngle (number)
28595 o endAngle (number)
28599 * Create a new Input
28600 * @param {Object} config The config object
28603 Roo.bootstrap.Graph = function(config){
28604 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28610 * The img click event for the img.
28611 * @param {Roo.EventObject} e
28617 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28628 //g_colors: this.colors,
28635 getAutoCreate : function(){
28646 onRender : function(ct,position){
28649 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28651 if (typeof(Raphael) == 'undefined') {
28652 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28656 this.raphael = Raphael(this.el.dom);
28658 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28659 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28660 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28661 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28663 r.text(160, 10, "Single Series Chart").attr(txtattr);
28664 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28665 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28666 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28668 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28669 r.barchart(330, 10, 300, 220, data1);
28670 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28671 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28674 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28675 // r.barchart(30, 30, 560, 250, xdata, {
28676 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28677 // axis : "0 0 1 1",
28678 // axisxlabels : xdata
28679 // //yvalues : cols,
28682 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28684 // this.load(null,xdata,{
28685 // axis : "0 0 1 1",
28686 // axisxlabels : xdata
28691 load : function(graphtype,xdata,opts)
28693 this.raphael.clear();
28695 graphtype = this.graphtype;
28700 var r = this.raphael,
28701 fin = function () {
28702 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28704 fout = function () {
28705 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28707 pfin = function() {
28708 this.sector.stop();
28709 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28712 this.label[0].stop();
28713 this.label[0].attr({ r: 7.5 });
28714 this.label[1].attr({ "font-weight": 800 });
28717 pfout = function() {
28718 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28721 this.label[0].animate({ r: 5 }, 500, "bounce");
28722 this.label[1].attr({ "font-weight": 400 });
28728 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28731 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28734 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28735 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28737 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28744 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28749 setTitle: function(o)
28754 initEvents: function() {
28757 this.el.on('click', this.onClick, this);
28761 onClick : function(e)
28763 Roo.log('img onclick');
28764 this.fireEvent('click', this, e);
28776 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28779 * @class Roo.bootstrap.dash.NumberBox
28780 * @extends Roo.bootstrap.Component
28781 * Bootstrap NumberBox class
28782 * @cfg {String} headline Box headline
28783 * @cfg {String} content Box content
28784 * @cfg {String} icon Box icon
28785 * @cfg {String} footer Footer text
28786 * @cfg {String} fhref Footer href
28789 * Create a new NumberBox
28790 * @param {Object} config The config object
28794 Roo.bootstrap.dash.NumberBox = function(config){
28795 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28799 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28808 getAutoCreate : function(){
28812 cls : 'small-box ',
28820 cls : 'roo-headline',
28821 html : this.headline
28825 cls : 'roo-content',
28826 html : this.content
28840 cls : 'ion ' + this.icon
28849 cls : 'small-box-footer',
28850 href : this.fhref || '#',
28854 cfg.cn.push(footer);
28861 onRender : function(ct,position){
28862 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28869 setHeadline: function (value)
28871 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28874 setFooter: function (value, href)
28876 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28879 this.el.select('a.small-box-footer',true).first().attr('href', href);
28884 setContent: function (value)
28886 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28889 initEvents: function()
28903 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28906 * @class Roo.bootstrap.dash.TabBox
28907 * @extends Roo.bootstrap.Component
28908 * Bootstrap TabBox class
28909 * @cfg {String} title Title of the TabBox
28910 * @cfg {String} icon Icon of the TabBox
28911 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28912 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28915 * Create a new TabBox
28916 * @param {Object} config The config object
28920 Roo.bootstrap.dash.TabBox = function(config){
28921 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28926 * When a pane is added
28927 * @param {Roo.bootstrap.dash.TabPane} pane
28931 * @event activatepane
28932 * When a pane is activated
28933 * @param {Roo.bootstrap.dash.TabPane} pane
28935 "activatepane" : true
28943 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28948 tabScrollable : false,
28950 getChildContainer : function()
28952 return this.el.select('.tab-content', true).first();
28955 getAutoCreate : function(){
28959 cls: 'pull-left header',
28967 cls: 'fa ' + this.icon
28973 cls: 'nav nav-tabs pull-right',
28979 if(this.tabScrollable){
28986 cls: 'nav nav-tabs pull-right',
28997 cls: 'nav-tabs-custom',
29002 cls: 'tab-content no-padding',
29010 initEvents : function()
29012 //Roo.log('add add pane handler');
29013 this.on('addpane', this.onAddPane, this);
29016 * Updates the box title
29017 * @param {String} html to set the title to.
29019 setTitle : function(value)
29021 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29023 onAddPane : function(pane)
29025 this.panes.push(pane);
29026 //Roo.log('addpane');
29028 // tabs are rendere left to right..
29029 if(!this.showtabs){
29033 var ctr = this.el.select('.nav-tabs', true).first();
29036 var existing = ctr.select('.nav-tab',true);
29037 var qty = existing.getCount();;
29040 var tab = ctr.createChild({
29042 cls : 'nav-tab' + (qty ? '' : ' active'),
29050 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29053 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29055 pane.el.addClass('active');
29060 onTabClick : function(ev,un,ob,pane)
29062 //Roo.log('tab - prev default');
29063 ev.preventDefault();
29066 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29067 pane.tab.addClass('active');
29068 //Roo.log(pane.title);
29069 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29070 // technically we should have a deactivate event.. but maybe add later.
29071 // and it should not de-activate the selected tab...
29072 this.fireEvent('activatepane', pane);
29073 pane.el.addClass('active');
29074 pane.fireEvent('activate');
29079 getActivePane : function()
29082 Roo.each(this.panes, function(p) {
29083 if(p.el.hasClass('active')){
29104 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29106 * @class Roo.bootstrap.TabPane
29107 * @extends Roo.bootstrap.Component
29108 * Bootstrap TabPane class
29109 * @cfg {Boolean} active (false | true) Default false
29110 * @cfg {String} title title of panel
29114 * Create a new TabPane
29115 * @param {Object} config The config object
29118 Roo.bootstrap.dash.TabPane = function(config){
29119 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29125 * When a pane is activated
29126 * @param {Roo.bootstrap.dash.TabPane} pane
29133 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29138 // the tabBox that this is attached to.
29141 getAutoCreate : function()
29149 cfg.cls += ' active';
29154 initEvents : function()
29156 //Roo.log('trigger add pane handler');
29157 this.parent().fireEvent('addpane', this)
29161 * Updates the tab title
29162 * @param {String} html to set the title to.
29164 setTitle: function(str)
29170 this.tab.select('a', true).first().dom.innerHTML = str;
29187 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29190 * @class Roo.bootstrap.menu.Menu
29191 * @extends Roo.bootstrap.Component
29192 * Bootstrap Menu class - container for Menu
29193 * @cfg {String} html Text of the menu
29194 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29195 * @cfg {String} icon Font awesome icon
29196 * @cfg {String} pos Menu align to (top | bottom) default bottom
29200 * Create a new Menu
29201 * @param {Object} config The config object
29205 Roo.bootstrap.menu.Menu = function(config){
29206 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29210 * @event beforeshow
29211 * Fires before this menu is displayed
29212 * @param {Roo.bootstrap.menu.Menu} this
29216 * @event beforehide
29217 * Fires before this menu is hidden
29218 * @param {Roo.bootstrap.menu.Menu} this
29223 * Fires after this menu is displayed
29224 * @param {Roo.bootstrap.menu.Menu} this
29229 * Fires after this menu is hidden
29230 * @param {Roo.bootstrap.menu.Menu} this
29235 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29236 * @param {Roo.bootstrap.menu.Menu} this
29237 * @param {Roo.EventObject} e
29244 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29248 weight : 'default',
29253 getChildContainer : function() {
29254 if(this.isSubMenu){
29258 return this.el.select('ul.dropdown-menu', true).first();
29261 getAutoCreate : function()
29266 cls : 'roo-menu-text',
29274 cls : 'fa ' + this.icon
29285 cls : 'dropdown-button btn btn-' + this.weight,
29290 cls : 'dropdown-toggle btn btn-' + this.weight,
29300 cls : 'dropdown-menu'
29306 if(this.pos == 'top'){
29307 cfg.cls += ' dropup';
29310 if(this.isSubMenu){
29313 cls : 'dropdown-menu'
29320 onRender : function(ct, position)
29322 this.isSubMenu = ct.hasClass('dropdown-submenu');
29324 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29327 initEvents : function()
29329 if(this.isSubMenu){
29333 this.hidden = true;
29335 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29336 this.triggerEl.on('click', this.onTriggerPress, this);
29338 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29339 this.buttonEl.on('click', this.onClick, this);
29345 if(this.isSubMenu){
29349 return this.el.select('ul.dropdown-menu', true).first();
29352 onClick : function(e)
29354 this.fireEvent("click", this, e);
29357 onTriggerPress : function(e)
29359 if (this.isVisible()) {
29366 isVisible : function(){
29367 return !this.hidden;
29372 this.fireEvent("beforeshow", this);
29374 this.hidden = false;
29375 this.el.addClass('open');
29377 Roo.get(document).on("mouseup", this.onMouseUp, this);
29379 this.fireEvent("show", this);
29386 this.fireEvent("beforehide", this);
29388 this.hidden = true;
29389 this.el.removeClass('open');
29391 Roo.get(document).un("mouseup", this.onMouseUp);
29393 this.fireEvent("hide", this);
29396 onMouseUp : function()
29410 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29413 * @class Roo.bootstrap.menu.Item
29414 * @extends Roo.bootstrap.Component
29415 * Bootstrap MenuItem class
29416 * @cfg {Boolean} submenu (true | false) default false
29417 * @cfg {String} html text of the item
29418 * @cfg {String} href the link
29419 * @cfg {Boolean} disable (true | false) default false
29420 * @cfg {Boolean} preventDefault (true | false) default true
29421 * @cfg {String} icon Font awesome icon
29422 * @cfg {String} pos Submenu align to (left | right) default right
29426 * Create a new Item
29427 * @param {Object} config The config object
29431 Roo.bootstrap.menu.Item = function(config){
29432 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29436 * Fires when the mouse is hovering over this menu
29437 * @param {Roo.bootstrap.menu.Item} this
29438 * @param {Roo.EventObject} e
29443 * Fires when the mouse exits this menu
29444 * @param {Roo.bootstrap.menu.Item} this
29445 * @param {Roo.EventObject} e
29451 * The raw click event for the entire grid.
29452 * @param {Roo.EventObject} e
29458 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29463 preventDefault: true,
29468 getAutoCreate : function()
29473 cls : 'roo-menu-item-text',
29481 cls : 'fa ' + this.icon
29490 href : this.href || '#',
29497 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29501 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29503 if(this.pos == 'left'){
29504 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29511 initEvents : function()
29513 this.el.on('mouseover', this.onMouseOver, this);
29514 this.el.on('mouseout', this.onMouseOut, this);
29516 this.el.select('a', true).first().on('click', this.onClick, this);
29520 onClick : function(e)
29522 if(this.preventDefault){
29523 e.preventDefault();
29526 this.fireEvent("click", this, e);
29529 onMouseOver : function(e)
29531 if(this.submenu && this.pos == 'left'){
29532 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29535 this.fireEvent("mouseover", this, e);
29538 onMouseOut : function(e)
29540 this.fireEvent("mouseout", this, e);
29552 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29555 * @class Roo.bootstrap.menu.Separator
29556 * @extends Roo.bootstrap.Component
29557 * Bootstrap Separator class
29560 * Create a new Separator
29561 * @param {Object} config The config object
29565 Roo.bootstrap.menu.Separator = function(config){
29566 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29569 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29571 getAutoCreate : function(){
29574 cls: 'dropdown-divider divider'
29592 * @class Roo.bootstrap.Tooltip
29593 * Bootstrap Tooltip class
29594 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29595 * to determine which dom element triggers the tooltip.
29597 * It needs to add support for additional attributes like tooltip-position
29600 * Create a new Toolti
29601 * @param {Object} config The config object
29604 Roo.bootstrap.Tooltip = function(config){
29605 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29607 this.alignment = Roo.bootstrap.Tooltip.alignment;
29609 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29610 this.alignment = config.alignment;
29615 Roo.apply(Roo.bootstrap.Tooltip, {
29617 * @function init initialize tooltip monitoring.
29621 currentTip : false,
29622 currentRegion : false,
29628 Roo.get(document).on('mouseover', this.enter ,this);
29629 Roo.get(document).on('mouseout', this.leave, this);
29632 this.currentTip = new Roo.bootstrap.Tooltip();
29635 enter : function(ev)
29637 var dom = ev.getTarget();
29639 //Roo.log(['enter',dom]);
29640 var el = Roo.fly(dom);
29641 if (this.currentEl) {
29643 //Roo.log(this.currentEl);
29644 //Roo.log(this.currentEl.contains(dom));
29645 if (this.currentEl == el) {
29648 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29654 if (this.currentTip.el) {
29655 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29659 if(!el || el.dom == document){
29665 if (!el.attr('tooltip')) {
29666 pel = el.findParent("[tooltip]");
29668 bindEl = Roo.get(pel);
29674 // you can not look for children, as if el is the body.. then everythign is the child..
29675 if (!pel && !el.attr('tooltip')) { //
29676 if (!el.select("[tooltip]").elements.length) {
29679 // is the mouse over this child...?
29680 bindEl = el.select("[tooltip]").first();
29681 var xy = ev.getXY();
29682 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29683 //Roo.log("not in region.");
29686 //Roo.log("child element over..");
29689 this.currentEl = el;
29690 this.currentTip.bind(bindEl);
29691 this.currentRegion = Roo.lib.Region.getRegion(dom);
29692 this.currentTip.enter();
29695 leave : function(ev)
29697 var dom = ev.getTarget();
29698 //Roo.log(['leave',dom]);
29699 if (!this.currentEl) {
29704 if (dom != this.currentEl.dom) {
29707 var xy = ev.getXY();
29708 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29711 // only activate leave if mouse cursor is outside... bounding box..
29716 if (this.currentTip) {
29717 this.currentTip.leave();
29719 //Roo.log('clear currentEl');
29720 this.currentEl = false;
29725 'left' : ['r-l', [-2,0], 'right'],
29726 'right' : ['l-r', [2,0], 'left'],
29727 'bottom' : ['t-b', [0,2], 'top'],
29728 'top' : [ 'b-t', [0,-2], 'bottom']
29734 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29739 delay : null, // can be { show : 300 , hide: 500}
29743 hoverState : null, //???
29745 placement : 'bottom',
29749 getAutoCreate : function(){
29756 cls : 'tooltip-arrow arrow'
29759 cls : 'tooltip-inner'
29766 bind : function(el)
29771 initEvents : function()
29773 this.arrowEl = this.el.select('.arrow', true).first();
29774 this.innerEl = this.el.select('.tooltip-inner', true).first();
29777 enter : function () {
29779 if (this.timeout != null) {
29780 clearTimeout(this.timeout);
29783 this.hoverState = 'in';
29784 //Roo.log("enter - show");
29785 if (!this.delay || !this.delay.show) {
29790 this.timeout = setTimeout(function () {
29791 if (_t.hoverState == 'in') {
29794 }, this.delay.show);
29798 clearTimeout(this.timeout);
29800 this.hoverState = 'out';
29801 if (!this.delay || !this.delay.hide) {
29807 this.timeout = setTimeout(function () {
29808 //Roo.log("leave - timeout");
29810 if (_t.hoverState == 'out') {
29812 Roo.bootstrap.Tooltip.currentEl = false;
29817 show : function (msg)
29820 this.render(document.body);
29823 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29825 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29827 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29829 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29830 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29832 var placement = typeof this.placement == 'function' ?
29833 this.placement.call(this, this.el, on_el) :
29836 var autoToken = /\s?auto?\s?/i;
29837 var autoPlace = autoToken.test(placement);
29839 placement = placement.replace(autoToken, '') || 'top';
29843 //this.el.setXY([0,0]);
29845 //this.el.dom.style.display='block';
29847 //this.el.appendTo(on_el);
29849 var p = this.getPosition();
29850 var box = this.el.getBox();
29856 var align = this.alignment[placement];
29858 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29860 if(placement == 'top' || placement == 'bottom'){
29862 placement = 'right';
29865 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29866 placement = 'left';
29869 var scroll = Roo.select('body', true).first().getScroll();
29871 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29875 align = this.alignment[placement];
29877 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29881 var elems = document.getElementsByTagName('div');
29882 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29883 for (var i = 0; i < elems.length; i++) {
29884 var zindex = Number.parseInt(
29885 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29888 if (zindex > highest) {
29895 this.el.dom.style.zIndex = highest;
29897 this.el.alignTo(this.bindEl, align[0],align[1]);
29898 //var arrow = this.el.select('.arrow',true).first();
29899 //arrow.set(align[2],
29901 this.el.addClass(placement);
29902 this.el.addClass("bs-tooltip-"+ placement);
29904 this.el.addClass('in fade show');
29906 this.hoverState = null;
29908 if (this.el.hasClass('fade')) {
29923 //this.el.setXY([0,0]);
29924 this.el.removeClass(['show', 'in']);
29940 * @class Roo.bootstrap.LocationPicker
29941 * @extends Roo.bootstrap.Component
29942 * Bootstrap LocationPicker class
29943 * @cfg {Number} latitude Position when init default 0
29944 * @cfg {Number} longitude Position when init default 0
29945 * @cfg {Number} zoom default 15
29946 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29947 * @cfg {Boolean} mapTypeControl default false
29948 * @cfg {Boolean} disableDoubleClickZoom default false
29949 * @cfg {Boolean} scrollwheel default true
29950 * @cfg {Boolean} streetViewControl default false
29951 * @cfg {Number} radius default 0
29952 * @cfg {String} locationName
29953 * @cfg {Boolean} draggable default true
29954 * @cfg {Boolean} enableAutocomplete default false
29955 * @cfg {Boolean} enableReverseGeocode default true
29956 * @cfg {String} markerTitle
29959 * Create a new LocationPicker
29960 * @param {Object} config The config object
29964 Roo.bootstrap.LocationPicker = function(config){
29966 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29971 * Fires when the picker initialized.
29972 * @param {Roo.bootstrap.LocationPicker} this
29973 * @param {Google Location} location
29977 * @event positionchanged
29978 * Fires when the picker position changed.
29979 * @param {Roo.bootstrap.LocationPicker} this
29980 * @param {Google Location} location
29982 positionchanged : true,
29985 * Fires when the map resize.
29986 * @param {Roo.bootstrap.LocationPicker} this
29991 * Fires when the map show.
29992 * @param {Roo.bootstrap.LocationPicker} this
29997 * Fires when the map hide.
29998 * @param {Roo.bootstrap.LocationPicker} this
30003 * Fires when click the map.
30004 * @param {Roo.bootstrap.LocationPicker} this
30005 * @param {Map event} e
30009 * @event mapRightClick
30010 * Fires when right click the map.
30011 * @param {Roo.bootstrap.LocationPicker} this
30012 * @param {Map event} e
30014 mapRightClick : true,
30016 * @event markerClick
30017 * Fires when click the marker.
30018 * @param {Roo.bootstrap.LocationPicker} this
30019 * @param {Map event} e
30021 markerClick : true,
30023 * @event markerRightClick
30024 * Fires when right click the marker.
30025 * @param {Roo.bootstrap.LocationPicker} this
30026 * @param {Map event} e
30028 markerRightClick : true,
30030 * @event OverlayViewDraw
30031 * Fires when OverlayView Draw
30032 * @param {Roo.bootstrap.LocationPicker} this
30034 OverlayViewDraw : true,
30036 * @event OverlayViewOnAdd
30037 * Fires when OverlayView Draw
30038 * @param {Roo.bootstrap.LocationPicker} this
30040 OverlayViewOnAdd : true,
30042 * @event OverlayViewOnRemove
30043 * Fires when OverlayView Draw
30044 * @param {Roo.bootstrap.LocationPicker} this
30046 OverlayViewOnRemove : true,
30048 * @event OverlayViewShow
30049 * Fires when OverlayView Draw
30050 * @param {Roo.bootstrap.LocationPicker} this
30051 * @param {Pixel} cpx
30053 OverlayViewShow : true,
30055 * @event OverlayViewHide
30056 * Fires when OverlayView Draw
30057 * @param {Roo.bootstrap.LocationPicker} this
30059 OverlayViewHide : true,
30061 * @event loadexception
30062 * Fires when load google lib failed.
30063 * @param {Roo.bootstrap.LocationPicker} this
30065 loadexception : true
30070 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30072 gMapContext: false,
30078 mapTypeControl: false,
30079 disableDoubleClickZoom: false,
30081 streetViewControl: false,
30085 enableAutocomplete: false,
30086 enableReverseGeocode: true,
30089 getAutoCreate: function()
30094 cls: 'roo-location-picker'
30100 initEvents: function(ct, position)
30102 if(!this.el.getWidth() || this.isApplied()){
30106 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30111 initial: function()
30113 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30114 this.fireEvent('loadexception', this);
30118 if(!this.mapTypeId){
30119 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30122 this.gMapContext = this.GMapContext();
30124 this.initOverlayView();
30126 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30130 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30131 _this.setPosition(_this.gMapContext.marker.position);
30134 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30135 _this.fireEvent('mapClick', this, event);
30139 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30140 _this.fireEvent('mapRightClick', this, event);
30144 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30145 _this.fireEvent('markerClick', this, event);
30149 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30150 _this.fireEvent('markerRightClick', this, event);
30154 this.setPosition(this.gMapContext.location);
30156 this.fireEvent('initial', this, this.gMapContext.location);
30159 initOverlayView: function()
30163 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30167 _this.fireEvent('OverlayViewDraw', _this);
30172 _this.fireEvent('OverlayViewOnAdd', _this);
30175 onRemove: function()
30177 _this.fireEvent('OverlayViewOnRemove', _this);
30180 show: function(cpx)
30182 _this.fireEvent('OverlayViewShow', _this, cpx);
30187 _this.fireEvent('OverlayViewHide', _this);
30193 fromLatLngToContainerPixel: function(event)
30195 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30198 isApplied: function()
30200 return this.getGmapContext() == false ? false : true;
30203 getGmapContext: function()
30205 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30208 GMapContext: function()
30210 var position = new google.maps.LatLng(this.latitude, this.longitude);
30212 var _map = new google.maps.Map(this.el.dom, {
30215 mapTypeId: this.mapTypeId,
30216 mapTypeControl: this.mapTypeControl,
30217 disableDoubleClickZoom: this.disableDoubleClickZoom,
30218 scrollwheel: this.scrollwheel,
30219 streetViewControl: this.streetViewControl,
30220 locationName: this.locationName,
30221 draggable: this.draggable,
30222 enableAutocomplete: this.enableAutocomplete,
30223 enableReverseGeocode: this.enableReverseGeocode
30226 var _marker = new google.maps.Marker({
30227 position: position,
30229 title: this.markerTitle,
30230 draggable: this.draggable
30237 location: position,
30238 radius: this.radius,
30239 locationName: this.locationName,
30240 addressComponents: {
30241 formatted_address: null,
30242 addressLine1: null,
30243 addressLine2: null,
30245 streetNumber: null,
30249 stateOrProvince: null
30252 domContainer: this.el.dom,
30253 geodecoder: new google.maps.Geocoder()
30257 drawCircle: function(center, radius, options)
30259 if (this.gMapContext.circle != null) {
30260 this.gMapContext.circle.setMap(null);
30264 options = Roo.apply({}, options, {
30265 strokeColor: "#0000FF",
30266 strokeOpacity: .35,
30268 fillColor: "#0000FF",
30272 options.map = this.gMapContext.map;
30273 options.radius = radius;
30274 options.center = center;
30275 this.gMapContext.circle = new google.maps.Circle(options);
30276 return this.gMapContext.circle;
30282 setPosition: function(location)
30284 this.gMapContext.location = location;
30285 this.gMapContext.marker.setPosition(location);
30286 this.gMapContext.map.panTo(location);
30287 this.drawCircle(location, this.gMapContext.radius, {});
30291 if (this.gMapContext.settings.enableReverseGeocode) {
30292 this.gMapContext.geodecoder.geocode({
30293 latLng: this.gMapContext.location
30294 }, function(results, status) {
30296 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30297 _this.gMapContext.locationName = results[0].formatted_address;
30298 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30300 _this.fireEvent('positionchanged', this, location);
30307 this.fireEvent('positionchanged', this, location);
30312 google.maps.event.trigger(this.gMapContext.map, "resize");
30314 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30316 this.fireEvent('resize', this);
30319 setPositionByLatLng: function(latitude, longitude)
30321 this.setPosition(new google.maps.LatLng(latitude, longitude));
30324 getCurrentPosition: function()
30327 latitude: this.gMapContext.location.lat(),
30328 longitude: this.gMapContext.location.lng()
30332 getAddressName: function()
30334 return this.gMapContext.locationName;
30337 getAddressComponents: function()
30339 return this.gMapContext.addressComponents;
30342 address_component_from_google_geocode: function(address_components)
30346 for (var i = 0; i < address_components.length; i++) {
30347 var component = address_components[i];
30348 if (component.types.indexOf("postal_code") >= 0) {
30349 result.postalCode = component.short_name;
30350 } else if (component.types.indexOf("street_number") >= 0) {
30351 result.streetNumber = component.short_name;
30352 } else if (component.types.indexOf("route") >= 0) {
30353 result.streetName = component.short_name;
30354 } else if (component.types.indexOf("neighborhood") >= 0) {
30355 result.city = component.short_name;
30356 } else if (component.types.indexOf("locality") >= 0) {
30357 result.city = component.short_name;
30358 } else if (component.types.indexOf("sublocality") >= 0) {
30359 result.district = component.short_name;
30360 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30361 result.stateOrProvince = component.short_name;
30362 } else if (component.types.indexOf("country") >= 0) {
30363 result.country = component.short_name;
30367 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30368 result.addressLine2 = "";
30372 setZoomLevel: function(zoom)
30374 this.gMapContext.map.setZoom(zoom);
30387 this.fireEvent('show', this);
30398 this.fireEvent('hide', this);
30403 Roo.apply(Roo.bootstrap.LocationPicker, {
30405 OverlayView : function(map, options)
30407 options = options || {};
30414 * @class Roo.bootstrap.Alert
30415 * @extends Roo.bootstrap.Component
30416 * Bootstrap Alert class - shows an alert area box
30418 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30419 Enter a valid email address
30422 * @cfg {String} title The title of alert
30423 * @cfg {String} html The content of alert
30424 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30425 * @cfg {String} fa font-awesomeicon
30426 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30427 * @cfg {Boolean} close true to show a x closer
30431 * Create a new alert
30432 * @param {Object} config The config object
30436 Roo.bootstrap.Alert = function(config){
30437 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30441 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30447 faicon: false, // BC
30451 getAutoCreate : function()
30463 style : this.close ? '' : 'display:none'
30467 cls : 'roo-alert-icon'
30472 cls : 'roo-alert-title',
30477 cls : 'roo-alert-text',
30484 cfg.cn[0].cls += ' fa ' + this.faicon;
30487 cfg.cn[0].cls += ' fa ' + this.fa;
30491 cfg.cls += ' alert-' + this.weight;
30497 initEvents: function()
30499 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30500 this.titleEl = this.el.select('.roo-alert-title',true).first();
30501 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30502 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30503 if (this.seconds > 0) {
30504 this.hide.defer(this.seconds, this);
30508 * Set the Title Message HTML
30509 * @param {String} html
30511 setTitle : function(str)
30513 this.titleEl.dom.innerHTML = str;
30517 * Set the Body Message HTML
30518 * @param {String} html
30520 setHtml : function(str)
30522 this.htmlEl.dom.innerHTML = str;
30525 * Set the Weight of the alert
30526 * @param {String} (success|info|warning|danger) weight
30529 setWeight : function(weight)
30532 this.el.removeClass('alert-' + this.weight);
30535 this.weight = weight;
30537 this.el.addClass('alert-' + this.weight);
30540 * Set the Icon of the alert
30541 * @param {String} see fontawsome names (name without the 'fa-' bit)
30543 setIcon : function(icon)
30546 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30549 this.faicon = icon;
30551 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30576 * @class Roo.bootstrap.UploadCropbox
30577 * @extends Roo.bootstrap.Component
30578 * Bootstrap UploadCropbox class
30579 * @cfg {String} emptyText show when image has been loaded
30580 * @cfg {String} rotateNotify show when image too small to rotate
30581 * @cfg {Number} errorTimeout default 3000
30582 * @cfg {Number} minWidth default 300
30583 * @cfg {Number} minHeight default 300
30584 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30585 * @cfg {Boolean} isDocument (true|false) default false
30586 * @cfg {String} url action url
30587 * @cfg {String} paramName default 'imageUpload'
30588 * @cfg {String} method default POST
30589 * @cfg {Boolean} loadMask (true|false) default true
30590 * @cfg {Boolean} loadingText default 'Loading...'
30593 * Create a new UploadCropbox
30594 * @param {Object} config The config object
30597 Roo.bootstrap.UploadCropbox = function(config){
30598 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30602 * @event beforeselectfile
30603 * Fire before select file
30604 * @param {Roo.bootstrap.UploadCropbox} this
30606 "beforeselectfile" : true,
30609 * Fire after initEvent
30610 * @param {Roo.bootstrap.UploadCropbox} this
30615 * Fire after initEvent
30616 * @param {Roo.bootstrap.UploadCropbox} this
30617 * @param {String} data
30622 * Fire when preparing the file data
30623 * @param {Roo.bootstrap.UploadCropbox} this
30624 * @param {Object} file
30629 * Fire when get exception
30630 * @param {Roo.bootstrap.UploadCropbox} this
30631 * @param {XMLHttpRequest} xhr
30633 "exception" : true,
30635 * @event beforeloadcanvas
30636 * Fire before load the canvas
30637 * @param {Roo.bootstrap.UploadCropbox} this
30638 * @param {String} src
30640 "beforeloadcanvas" : true,
30643 * Fire when trash image
30644 * @param {Roo.bootstrap.UploadCropbox} this
30649 * Fire when download the image
30650 * @param {Roo.bootstrap.UploadCropbox} this
30654 * @event footerbuttonclick
30655 * Fire when footerbuttonclick
30656 * @param {Roo.bootstrap.UploadCropbox} this
30657 * @param {String} type
30659 "footerbuttonclick" : true,
30663 * @param {Roo.bootstrap.UploadCropbox} this
30668 * Fire when rotate the image
30669 * @param {Roo.bootstrap.UploadCropbox} this
30670 * @param {String} pos
30675 * Fire when inspect the file
30676 * @param {Roo.bootstrap.UploadCropbox} this
30677 * @param {Object} file
30682 * Fire when xhr upload the file
30683 * @param {Roo.bootstrap.UploadCropbox} this
30684 * @param {Object} data
30689 * Fire when arrange the file data
30690 * @param {Roo.bootstrap.UploadCropbox} this
30691 * @param {Object} formData
30696 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30699 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30701 emptyText : 'Click to upload image',
30702 rotateNotify : 'Image is too small to rotate',
30703 errorTimeout : 3000,
30717 cropType : 'image/jpeg',
30719 canvasLoaded : false,
30720 isDocument : false,
30722 paramName : 'imageUpload',
30724 loadingText : 'Loading...',
30727 getAutoCreate : function()
30731 cls : 'roo-upload-cropbox',
30735 cls : 'roo-upload-cropbox-selector',
30740 cls : 'roo-upload-cropbox-body',
30741 style : 'cursor:pointer',
30745 cls : 'roo-upload-cropbox-preview'
30749 cls : 'roo-upload-cropbox-thumb'
30753 cls : 'roo-upload-cropbox-empty-notify',
30754 html : this.emptyText
30758 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30759 html : this.rotateNotify
30765 cls : 'roo-upload-cropbox-footer',
30768 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30778 onRender : function(ct, position)
30780 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30782 if (this.buttons.length) {
30784 Roo.each(this.buttons, function(bb) {
30786 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30788 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30794 this.maskEl = this.el;
30798 initEvents : function()
30800 this.urlAPI = (window.createObjectURL && window) ||
30801 (window.URL && URL.revokeObjectURL && URL) ||
30802 (window.webkitURL && webkitURL);
30804 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30805 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30807 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30808 this.selectorEl.hide();
30810 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30811 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30813 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30814 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30815 this.thumbEl.hide();
30817 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30818 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30820 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30821 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30822 this.errorEl.hide();
30824 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30825 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30826 this.footerEl.hide();
30828 this.setThumbBoxSize();
30834 this.fireEvent('initial', this);
30841 window.addEventListener("resize", function() { _this.resize(); } );
30843 this.bodyEl.on('click', this.beforeSelectFile, this);
30846 this.bodyEl.on('touchstart', this.onTouchStart, this);
30847 this.bodyEl.on('touchmove', this.onTouchMove, this);
30848 this.bodyEl.on('touchend', this.onTouchEnd, this);
30852 this.bodyEl.on('mousedown', this.onMouseDown, this);
30853 this.bodyEl.on('mousemove', this.onMouseMove, this);
30854 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30855 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30856 Roo.get(document).on('mouseup', this.onMouseUp, this);
30859 this.selectorEl.on('change', this.onFileSelected, this);
30865 this.baseScale = 1;
30867 this.baseRotate = 1;
30868 this.dragable = false;
30869 this.pinching = false;
30872 this.cropData = false;
30873 this.notifyEl.dom.innerHTML = this.emptyText;
30875 this.selectorEl.dom.value = '';
30879 resize : function()
30881 if(this.fireEvent('resize', this) != false){
30882 this.setThumbBoxPosition();
30883 this.setCanvasPosition();
30887 onFooterButtonClick : function(e, el, o, type)
30890 case 'rotate-left' :
30891 this.onRotateLeft(e);
30893 case 'rotate-right' :
30894 this.onRotateRight(e);
30897 this.beforeSelectFile(e);
30912 this.fireEvent('footerbuttonclick', this, type);
30915 beforeSelectFile : function(e)
30917 e.preventDefault();
30919 if(this.fireEvent('beforeselectfile', this) != false){
30920 this.selectorEl.dom.click();
30924 onFileSelected : function(e)
30926 e.preventDefault();
30928 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30932 var file = this.selectorEl.dom.files[0];
30934 if(this.fireEvent('inspect', this, file) != false){
30935 this.prepare(file);
30940 trash : function(e)
30942 this.fireEvent('trash', this);
30945 download : function(e)
30947 this.fireEvent('download', this);
30950 loadCanvas : function(src)
30952 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30956 this.imageEl = document.createElement('img');
30960 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30962 this.imageEl.src = src;
30966 onLoadCanvas : function()
30968 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30969 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30971 this.bodyEl.un('click', this.beforeSelectFile, this);
30973 this.notifyEl.hide();
30974 this.thumbEl.show();
30975 this.footerEl.show();
30977 this.baseRotateLevel();
30979 if(this.isDocument){
30980 this.setThumbBoxSize();
30983 this.setThumbBoxPosition();
30985 this.baseScaleLevel();
30991 this.canvasLoaded = true;
30994 this.maskEl.unmask();
30999 setCanvasPosition : function()
31001 if(!this.canvasEl){
31005 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31006 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31008 this.previewEl.setLeft(pw);
31009 this.previewEl.setTop(ph);
31013 onMouseDown : function(e)
31017 this.dragable = true;
31018 this.pinching = false;
31020 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31021 this.dragable = false;
31025 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31026 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31030 onMouseMove : function(e)
31034 if(!this.canvasLoaded){
31038 if (!this.dragable){
31042 var minX = Math.ceil(this.thumbEl.getLeft(true));
31043 var minY = Math.ceil(this.thumbEl.getTop(true));
31045 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31046 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31048 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31049 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31051 x = x - this.mouseX;
31052 y = y - this.mouseY;
31054 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31055 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31057 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31058 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31060 this.previewEl.setLeft(bgX);
31061 this.previewEl.setTop(bgY);
31063 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31064 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31067 onMouseUp : function(e)
31071 this.dragable = false;
31074 onMouseWheel : function(e)
31078 this.startScale = this.scale;
31080 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31082 if(!this.zoomable()){
31083 this.scale = this.startScale;
31092 zoomable : function()
31094 var minScale = this.thumbEl.getWidth() / this.minWidth;
31096 if(this.minWidth < this.minHeight){
31097 minScale = this.thumbEl.getHeight() / this.minHeight;
31100 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31101 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31105 (this.rotate == 0 || this.rotate == 180) &&
31107 width > this.imageEl.OriginWidth ||
31108 height > this.imageEl.OriginHeight ||
31109 (width < this.minWidth && height < this.minHeight)
31117 (this.rotate == 90 || this.rotate == 270) &&
31119 width > this.imageEl.OriginWidth ||
31120 height > this.imageEl.OriginHeight ||
31121 (width < this.minHeight && height < this.minWidth)
31128 !this.isDocument &&
31129 (this.rotate == 0 || this.rotate == 180) &&
31131 width < this.minWidth ||
31132 width > this.imageEl.OriginWidth ||
31133 height < this.minHeight ||
31134 height > this.imageEl.OriginHeight
31141 !this.isDocument &&
31142 (this.rotate == 90 || this.rotate == 270) &&
31144 width < this.minHeight ||
31145 width > this.imageEl.OriginWidth ||
31146 height < this.minWidth ||
31147 height > this.imageEl.OriginHeight
31157 onRotateLeft : function(e)
31159 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31161 var minScale = this.thumbEl.getWidth() / this.minWidth;
31163 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31164 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31166 this.startScale = this.scale;
31168 while (this.getScaleLevel() < minScale){
31170 this.scale = this.scale + 1;
31172 if(!this.zoomable()){
31177 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31178 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31183 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31190 this.scale = this.startScale;
31192 this.onRotateFail();
31197 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31199 if(this.isDocument){
31200 this.setThumbBoxSize();
31201 this.setThumbBoxPosition();
31202 this.setCanvasPosition();
31207 this.fireEvent('rotate', this, 'left');
31211 onRotateRight : function(e)
31213 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31215 var minScale = this.thumbEl.getWidth() / this.minWidth;
31217 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31218 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31220 this.startScale = this.scale;
31222 while (this.getScaleLevel() < minScale){
31224 this.scale = this.scale + 1;
31226 if(!this.zoomable()){
31231 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31232 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31237 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31244 this.scale = this.startScale;
31246 this.onRotateFail();
31251 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31253 if(this.isDocument){
31254 this.setThumbBoxSize();
31255 this.setThumbBoxPosition();
31256 this.setCanvasPosition();
31261 this.fireEvent('rotate', this, 'right');
31264 onRotateFail : function()
31266 this.errorEl.show(true);
31270 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31275 this.previewEl.dom.innerHTML = '';
31277 var canvasEl = document.createElement("canvas");
31279 var contextEl = canvasEl.getContext("2d");
31281 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31282 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31283 var center = this.imageEl.OriginWidth / 2;
31285 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31286 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31287 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31288 center = this.imageEl.OriginHeight / 2;
31291 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31293 contextEl.translate(center, center);
31294 contextEl.rotate(this.rotate * Math.PI / 180);
31296 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31298 this.canvasEl = document.createElement("canvas");
31300 this.contextEl = this.canvasEl.getContext("2d");
31302 switch (this.rotate) {
31305 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31306 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31308 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31313 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31314 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31316 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31317 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31321 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31326 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31327 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31329 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31330 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.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);
31339 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31340 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31342 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31343 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31347 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);
31354 this.previewEl.appendChild(this.canvasEl);
31356 this.setCanvasPosition();
31361 if(!this.canvasLoaded){
31365 var imageCanvas = document.createElement("canvas");
31367 var imageContext = imageCanvas.getContext("2d");
31369 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31370 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31372 var center = imageCanvas.width / 2;
31374 imageContext.translate(center, center);
31376 imageContext.rotate(this.rotate * Math.PI / 180);
31378 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31380 var canvas = document.createElement("canvas");
31382 var context = canvas.getContext("2d");
31384 canvas.width = this.minWidth;
31385 canvas.height = this.minHeight;
31387 switch (this.rotate) {
31390 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31391 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31393 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31394 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31396 var targetWidth = this.minWidth - 2 * x;
31397 var targetHeight = this.minHeight - 2 * y;
31401 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31402 scale = targetWidth / width;
31405 if(x > 0 && y == 0){
31406 scale = targetHeight / height;
31409 if(x > 0 && y > 0){
31410 scale = targetWidth / width;
31412 if(width < height){
31413 scale = targetHeight / height;
31417 context.scale(scale, scale);
31419 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31420 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31422 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31423 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31425 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31430 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31431 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31433 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31434 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31436 var targetWidth = this.minWidth - 2 * x;
31437 var targetHeight = this.minHeight - 2 * y;
31441 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31442 scale = targetWidth / width;
31445 if(x > 0 && y == 0){
31446 scale = targetHeight / height;
31449 if(x > 0 && y > 0){
31450 scale = targetWidth / width;
31452 if(width < height){
31453 scale = targetHeight / height;
31457 context.scale(scale, scale);
31459 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31460 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31462 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31463 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31465 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31467 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31472 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31473 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31475 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31476 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31478 var targetWidth = this.minWidth - 2 * x;
31479 var targetHeight = this.minHeight - 2 * y;
31483 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31484 scale = targetWidth / width;
31487 if(x > 0 && y == 0){
31488 scale = targetHeight / height;
31491 if(x > 0 && y > 0){
31492 scale = targetWidth / width;
31494 if(width < height){
31495 scale = targetHeight / height;
31499 context.scale(scale, scale);
31501 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31502 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31504 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31505 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31507 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31508 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31510 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31515 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31516 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31518 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31519 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31521 var targetWidth = this.minWidth - 2 * x;
31522 var targetHeight = this.minHeight - 2 * y;
31526 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31527 scale = targetWidth / width;
31530 if(x > 0 && y == 0){
31531 scale = targetHeight / height;
31534 if(x > 0 && y > 0){
31535 scale = targetWidth / width;
31537 if(width < height){
31538 scale = targetHeight / height;
31542 context.scale(scale, scale);
31544 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31545 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31547 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31548 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31550 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31552 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31559 this.cropData = canvas.toDataURL(this.cropType);
31561 if(this.fireEvent('crop', this, this.cropData) !== false){
31562 this.process(this.file, this.cropData);
31569 setThumbBoxSize : function()
31573 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31574 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31575 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31577 this.minWidth = width;
31578 this.minHeight = height;
31580 if(this.rotate == 90 || this.rotate == 270){
31581 this.minWidth = height;
31582 this.minHeight = width;
31587 width = Math.ceil(this.minWidth * height / this.minHeight);
31589 if(this.minWidth > this.minHeight){
31591 height = Math.ceil(this.minHeight * width / this.minWidth);
31594 this.thumbEl.setStyle({
31595 width : width + 'px',
31596 height : height + 'px'
31603 setThumbBoxPosition : function()
31605 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31606 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31608 this.thumbEl.setLeft(x);
31609 this.thumbEl.setTop(y);
31613 baseRotateLevel : function()
31615 this.baseRotate = 1;
31618 typeof(this.exif) != 'undefined' &&
31619 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31620 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31622 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31625 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31629 baseScaleLevel : function()
31633 if(this.isDocument){
31635 if(this.baseRotate == 6 || this.baseRotate == 8){
31637 height = this.thumbEl.getHeight();
31638 this.baseScale = height / this.imageEl.OriginWidth;
31640 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31641 width = this.thumbEl.getWidth();
31642 this.baseScale = width / this.imageEl.OriginHeight;
31648 height = this.thumbEl.getHeight();
31649 this.baseScale = height / this.imageEl.OriginHeight;
31651 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31652 width = this.thumbEl.getWidth();
31653 this.baseScale = width / this.imageEl.OriginWidth;
31659 if(this.baseRotate == 6 || this.baseRotate == 8){
31661 width = this.thumbEl.getHeight();
31662 this.baseScale = width / this.imageEl.OriginHeight;
31664 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31665 height = this.thumbEl.getWidth();
31666 this.baseScale = height / this.imageEl.OriginHeight;
31669 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31670 height = this.thumbEl.getWidth();
31671 this.baseScale = height / this.imageEl.OriginHeight;
31673 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31674 width = this.thumbEl.getHeight();
31675 this.baseScale = width / this.imageEl.OriginWidth;
31682 width = this.thumbEl.getWidth();
31683 this.baseScale = width / this.imageEl.OriginWidth;
31685 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31686 height = this.thumbEl.getHeight();
31687 this.baseScale = height / this.imageEl.OriginHeight;
31690 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31692 height = this.thumbEl.getHeight();
31693 this.baseScale = height / this.imageEl.OriginHeight;
31695 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31696 width = this.thumbEl.getWidth();
31697 this.baseScale = width / this.imageEl.OriginWidth;
31705 getScaleLevel : function()
31707 return this.baseScale * Math.pow(1.1, this.scale);
31710 onTouchStart : function(e)
31712 if(!this.canvasLoaded){
31713 this.beforeSelectFile(e);
31717 var touches = e.browserEvent.touches;
31723 if(touches.length == 1){
31724 this.onMouseDown(e);
31728 if(touches.length != 2){
31734 for(var i = 0, finger; finger = touches[i]; i++){
31735 coords.push(finger.pageX, finger.pageY);
31738 var x = Math.pow(coords[0] - coords[2], 2);
31739 var y = Math.pow(coords[1] - coords[3], 2);
31741 this.startDistance = Math.sqrt(x + y);
31743 this.startScale = this.scale;
31745 this.pinching = true;
31746 this.dragable = false;
31750 onTouchMove : function(e)
31752 if(!this.pinching && !this.dragable){
31756 var touches = e.browserEvent.touches;
31763 this.onMouseMove(e);
31769 for(var i = 0, finger; finger = touches[i]; i++){
31770 coords.push(finger.pageX, finger.pageY);
31773 var x = Math.pow(coords[0] - coords[2], 2);
31774 var y = Math.pow(coords[1] - coords[3], 2);
31776 this.endDistance = Math.sqrt(x + y);
31778 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31780 if(!this.zoomable()){
31781 this.scale = this.startScale;
31789 onTouchEnd : function(e)
31791 this.pinching = false;
31792 this.dragable = false;
31796 process : function(file, crop)
31799 this.maskEl.mask(this.loadingText);
31802 this.xhr = new XMLHttpRequest();
31804 file.xhr = this.xhr;
31806 this.xhr.open(this.method, this.url, true);
31809 "Accept": "application/json",
31810 "Cache-Control": "no-cache",
31811 "X-Requested-With": "XMLHttpRequest"
31814 for (var headerName in headers) {
31815 var headerValue = headers[headerName];
31817 this.xhr.setRequestHeader(headerName, headerValue);
31823 this.xhr.onload = function()
31825 _this.xhrOnLoad(_this.xhr);
31828 this.xhr.onerror = function()
31830 _this.xhrOnError(_this.xhr);
31833 var formData = new FormData();
31835 formData.append('returnHTML', 'NO');
31838 formData.append('crop', crop);
31841 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31842 formData.append(this.paramName, file, file.name);
31845 if(typeof(file.filename) != 'undefined'){
31846 formData.append('filename', file.filename);
31849 if(typeof(file.mimetype) != 'undefined'){
31850 formData.append('mimetype', file.mimetype);
31853 if(this.fireEvent('arrange', this, formData) != false){
31854 this.xhr.send(formData);
31858 xhrOnLoad : function(xhr)
31861 this.maskEl.unmask();
31864 if (xhr.readyState !== 4) {
31865 this.fireEvent('exception', this, xhr);
31869 var response = Roo.decode(xhr.responseText);
31871 if(!response.success){
31872 this.fireEvent('exception', this, xhr);
31876 var response = Roo.decode(xhr.responseText);
31878 this.fireEvent('upload', this, response);
31882 xhrOnError : function()
31885 this.maskEl.unmask();
31888 Roo.log('xhr on error');
31890 var response = Roo.decode(xhr.responseText);
31896 prepare : function(file)
31899 this.maskEl.mask(this.loadingText);
31905 if(typeof(file) === 'string'){
31906 this.loadCanvas(file);
31910 if(!file || !this.urlAPI){
31915 this.cropType = file.type;
31919 if(this.fireEvent('prepare', this, this.file) != false){
31921 var reader = new FileReader();
31923 reader.onload = function (e) {
31924 if (e.target.error) {
31925 Roo.log(e.target.error);
31929 var buffer = e.target.result,
31930 dataView = new DataView(buffer),
31932 maxOffset = dataView.byteLength - 4,
31936 if (dataView.getUint16(0) === 0xffd8) {
31937 while (offset < maxOffset) {
31938 markerBytes = dataView.getUint16(offset);
31940 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31941 markerLength = dataView.getUint16(offset + 2) + 2;
31942 if (offset + markerLength > dataView.byteLength) {
31943 Roo.log('Invalid meta data: Invalid segment size.');
31947 if(markerBytes == 0xffe1){
31948 _this.parseExifData(
31955 offset += markerLength;
31965 var url = _this.urlAPI.createObjectURL(_this.file);
31967 _this.loadCanvas(url);
31972 reader.readAsArrayBuffer(this.file);
31978 parseExifData : function(dataView, offset, length)
31980 var tiffOffset = offset + 10,
31984 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31985 // No Exif data, might be XMP data instead
31989 // Check for the ASCII code for "Exif" (0x45786966):
31990 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31991 // No Exif data, might be XMP data instead
31994 if (tiffOffset + 8 > dataView.byteLength) {
31995 Roo.log('Invalid Exif data: Invalid segment size.');
31998 // Check for the two null bytes:
31999 if (dataView.getUint16(offset + 8) !== 0x0000) {
32000 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32003 // Check the byte alignment:
32004 switch (dataView.getUint16(tiffOffset)) {
32006 littleEndian = true;
32009 littleEndian = false;
32012 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32015 // Check for the TIFF tag marker (0x002A):
32016 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32017 Roo.log('Invalid Exif data: Missing TIFF marker.');
32020 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32021 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32023 this.parseExifTags(
32026 tiffOffset + dirOffset,
32031 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32036 if (dirOffset + 6 > dataView.byteLength) {
32037 Roo.log('Invalid Exif data: Invalid directory offset.');
32040 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32041 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32042 if (dirEndOffset + 4 > dataView.byteLength) {
32043 Roo.log('Invalid Exif data: Invalid directory size.');
32046 for (i = 0; i < tagsNumber; i += 1) {
32050 dirOffset + 2 + 12 * i, // tag offset
32054 // Return the offset to the next directory:
32055 return dataView.getUint32(dirEndOffset, littleEndian);
32058 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32060 var tag = dataView.getUint16(offset, littleEndian);
32062 this.exif[tag] = this.getExifValue(
32066 dataView.getUint16(offset + 2, littleEndian), // tag type
32067 dataView.getUint32(offset + 4, littleEndian), // tag length
32072 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32074 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32083 Roo.log('Invalid Exif data: Invalid tag type.');
32087 tagSize = tagType.size * length;
32088 // Determine if the value is contained in the dataOffset bytes,
32089 // or if the value at the dataOffset is a pointer to the actual data:
32090 dataOffset = tagSize > 4 ?
32091 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32092 if (dataOffset + tagSize > dataView.byteLength) {
32093 Roo.log('Invalid Exif data: Invalid data offset.');
32096 if (length === 1) {
32097 return tagType.getValue(dataView, dataOffset, littleEndian);
32100 for (i = 0; i < length; i += 1) {
32101 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32104 if (tagType.ascii) {
32106 // Concatenate the chars:
32107 for (i = 0; i < values.length; i += 1) {
32109 // Ignore the terminating NULL byte(s):
32110 if (c === '\u0000') {
32122 Roo.apply(Roo.bootstrap.UploadCropbox, {
32124 'Orientation': 0x0112
32128 1: 0, //'top-left',
32130 3: 180, //'bottom-right',
32131 // 4: 'bottom-left',
32133 6: 90, //'right-top',
32134 // 7: 'right-bottom',
32135 8: 270 //'left-bottom'
32139 // byte, 8-bit unsigned int:
32141 getValue: function (dataView, dataOffset) {
32142 return dataView.getUint8(dataOffset);
32146 // ascii, 8-bit byte:
32148 getValue: function (dataView, dataOffset) {
32149 return String.fromCharCode(dataView.getUint8(dataOffset));
32154 // short, 16 bit int:
32156 getValue: function (dataView, dataOffset, littleEndian) {
32157 return dataView.getUint16(dataOffset, littleEndian);
32161 // long, 32 bit int:
32163 getValue: function (dataView, dataOffset, littleEndian) {
32164 return dataView.getUint32(dataOffset, littleEndian);
32168 // rational = two long values, first is numerator, second is denominator:
32170 getValue: function (dataView, dataOffset, littleEndian) {
32171 return dataView.getUint32(dataOffset, littleEndian) /
32172 dataView.getUint32(dataOffset + 4, littleEndian);
32176 // slong, 32 bit signed int:
32178 getValue: function (dataView, dataOffset, littleEndian) {
32179 return dataView.getInt32(dataOffset, littleEndian);
32183 // srational, two slongs, first is numerator, second is denominator:
32185 getValue: function (dataView, dataOffset, littleEndian) {
32186 return dataView.getInt32(dataOffset, littleEndian) /
32187 dataView.getInt32(dataOffset + 4, littleEndian);
32197 cls : 'btn-group roo-upload-cropbox-rotate-left',
32198 action : 'rotate-left',
32202 cls : 'btn btn-default',
32203 html : '<i class="fa fa-undo"></i>'
32209 cls : 'btn-group roo-upload-cropbox-picture',
32210 action : 'picture',
32214 cls : 'btn btn-default',
32215 html : '<i class="fa fa-picture-o"></i>'
32221 cls : 'btn-group roo-upload-cropbox-rotate-right',
32222 action : 'rotate-right',
32226 cls : 'btn btn-default',
32227 html : '<i class="fa fa-repeat"></i>'
32235 cls : 'btn-group roo-upload-cropbox-rotate-left',
32236 action : 'rotate-left',
32240 cls : 'btn btn-default',
32241 html : '<i class="fa fa-undo"></i>'
32247 cls : 'btn-group roo-upload-cropbox-download',
32248 action : 'download',
32252 cls : 'btn btn-default',
32253 html : '<i class="fa fa-download"></i>'
32259 cls : 'btn-group roo-upload-cropbox-crop',
32264 cls : 'btn btn-default',
32265 html : '<i class="fa fa-crop"></i>'
32271 cls : 'btn-group roo-upload-cropbox-trash',
32276 cls : 'btn btn-default',
32277 html : '<i class="fa fa-trash"></i>'
32283 cls : 'btn-group roo-upload-cropbox-rotate-right',
32284 action : 'rotate-right',
32288 cls : 'btn btn-default',
32289 html : '<i class="fa fa-repeat"></i>'
32297 cls : 'btn-group roo-upload-cropbox-rotate-left',
32298 action : 'rotate-left',
32302 cls : 'btn btn-default',
32303 html : '<i class="fa fa-undo"></i>'
32309 cls : 'btn-group roo-upload-cropbox-rotate-right',
32310 action : 'rotate-right',
32314 cls : 'btn btn-default',
32315 html : '<i class="fa fa-repeat"></i>'
32328 * @class Roo.bootstrap.DocumentManager
32329 * @extends Roo.bootstrap.Component
32330 * Bootstrap DocumentManager class
32331 * @cfg {String} paramName default 'imageUpload'
32332 * @cfg {String} toolTipName default 'filename'
32333 * @cfg {String} method default POST
32334 * @cfg {String} url action url
32335 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32336 * @cfg {Boolean} multiple multiple upload default true
32337 * @cfg {Number} thumbSize default 300
32338 * @cfg {String} fieldLabel
32339 * @cfg {Number} labelWidth default 4
32340 * @cfg {String} labelAlign (left|top) default left
32341 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32342 * @cfg {Number} labellg set the width of label (1-12)
32343 * @cfg {Number} labelmd set the width of label (1-12)
32344 * @cfg {Number} labelsm set the width of label (1-12)
32345 * @cfg {Number} labelxs set the width of label (1-12)
32348 * Create a new DocumentManager
32349 * @param {Object} config The config object
32352 Roo.bootstrap.DocumentManager = function(config){
32353 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32356 this.delegates = [];
32361 * Fire when initial the DocumentManager
32362 * @param {Roo.bootstrap.DocumentManager} this
32367 * inspect selected file
32368 * @param {Roo.bootstrap.DocumentManager} this
32369 * @param {File} file
32374 * Fire when xhr load exception
32375 * @param {Roo.bootstrap.DocumentManager} this
32376 * @param {XMLHttpRequest} xhr
32378 "exception" : true,
32380 * @event afterupload
32381 * Fire when xhr load exception
32382 * @param {Roo.bootstrap.DocumentManager} this
32383 * @param {XMLHttpRequest} xhr
32385 "afterupload" : true,
32388 * prepare the form data
32389 * @param {Roo.bootstrap.DocumentManager} this
32390 * @param {Object} formData
32395 * Fire when remove the file
32396 * @param {Roo.bootstrap.DocumentManager} this
32397 * @param {Object} file
32402 * Fire after refresh the file
32403 * @param {Roo.bootstrap.DocumentManager} this
32408 * Fire after click the image
32409 * @param {Roo.bootstrap.DocumentManager} this
32410 * @param {Object} file
32415 * Fire when upload a image and editable set to true
32416 * @param {Roo.bootstrap.DocumentManager} this
32417 * @param {Object} file
32421 * @event beforeselectfile
32422 * Fire before select file
32423 * @param {Roo.bootstrap.DocumentManager} this
32425 "beforeselectfile" : true,
32428 * Fire before process file
32429 * @param {Roo.bootstrap.DocumentManager} this
32430 * @param {Object} file
32434 * @event previewrendered
32435 * Fire when preview rendered
32436 * @param {Roo.bootstrap.DocumentManager} this
32437 * @param {Object} file
32439 "previewrendered" : true,
32442 "previewResize" : true
32447 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32456 paramName : 'imageUpload',
32457 toolTipName : 'filename',
32460 labelAlign : 'left',
32470 getAutoCreate : function()
32472 var managerWidget = {
32474 cls : 'roo-document-manager',
32478 cls : 'roo-document-manager-selector',
32483 cls : 'roo-document-manager-uploader',
32487 cls : 'roo-document-manager-upload-btn',
32488 html : '<i class="fa fa-plus"></i>'
32499 cls : 'column col-md-12',
32504 if(this.fieldLabel.length){
32509 cls : 'column col-md-12',
32510 html : this.fieldLabel
32514 cls : 'column col-md-12',
32519 if(this.labelAlign == 'left'){
32524 html : this.fieldLabel
32533 if(this.labelWidth > 12){
32534 content[0].style = "width: " + this.labelWidth + 'px';
32537 if(this.labelWidth < 13 && this.labelmd == 0){
32538 this.labelmd = this.labelWidth;
32541 if(this.labellg > 0){
32542 content[0].cls += ' col-lg-' + this.labellg;
32543 content[1].cls += ' col-lg-' + (12 - this.labellg);
32546 if(this.labelmd > 0){
32547 content[0].cls += ' col-md-' + this.labelmd;
32548 content[1].cls += ' col-md-' + (12 - this.labelmd);
32551 if(this.labelsm > 0){
32552 content[0].cls += ' col-sm-' + this.labelsm;
32553 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32556 if(this.labelxs > 0){
32557 content[0].cls += ' col-xs-' + this.labelxs;
32558 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32566 cls : 'row clearfix',
32574 initEvents : function()
32576 this.managerEl = this.el.select('.roo-document-manager', true).first();
32577 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32579 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32580 this.selectorEl.hide();
32583 this.selectorEl.attr('multiple', 'multiple');
32586 this.selectorEl.on('change', this.onFileSelected, this);
32588 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32589 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32591 this.uploader.on('click', this.onUploaderClick, this);
32593 this.renderProgressDialog();
32597 window.addEventListener("resize", function() { _this.refresh(); } );
32599 this.fireEvent('initial', this);
32602 renderProgressDialog : function()
32606 this.progressDialog = new Roo.bootstrap.Modal({
32607 cls : 'roo-document-manager-progress-dialog',
32608 allow_close : false,
32619 btnclick : function() {
32620 _this.uploadCancel();
32626 this.progressDialog.render(Roo.get(document.body));
32628 this.progress = new Roo.bootstrap.Progress({
32629 cls : 'roo-document-manager-progress',
32634 this.progress.render(this.progressDialog.getChildContainer());
32636 this.progressBar = new Roo.bootstrap.ProgressBar({
32637 cls : 'roo-document-manager-progress-bar',
32640 aria_valuemax : 12,
32644 this.progressBar.render(this.progress.getChildContainer());
32647 onUploaderClick : function(e)
32649 e.preventDefault();
32651 if(this.fireEvent('beforeselectfile', this) != false){
32652 this.selectorEl.dom.click();
32657 onFileSelected : function(e)
32659 e.preventDefault();
32661 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32665 Roo.each(this.selectorEl.dom.files, function(file){
32666 if(this.fireEvent('inspect', this, file) != false){
32667 this.files.push(file);
32677 this.selectorEl.dom.value = '';
32679 if(!this.files || !this.files.length){
32683 if(this.boxes > 0 && this.files.length > this.boxes){
32684 this.files = this.files.slice(0, this.boxes);
32687 this.uploader.show();
32689 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32690 this.uploader.hide();
32699 Roo.each(this.files, function(file){
32701 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32702 var f = this.renderPreview(file);
32707 if(file.type.indexOf('image') != -1){
32708 this.delegates.push(
32710 _this.process(file);
32711 }).createDelegate(this)
32719 _this.process(file);
32720 }).createDelegate(this)
32725 this.files = files;
32727 this.delegates = this.delegates.concat(docs);
32729 if(!this.delegates.length){
32734 this.progressBar.aria_valuemax = this.delegates.length;
32741 arrange : function()
32743 if(!this.delegates.length){
32744 this.progressDialog.hide();
32749 var delegate = this.delegates.shift();
32751 this.progressDialog.show();
32753 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32755 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32760 refresh : function()
32762 this.uploader.show();
32764 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32765 this.uploader.hide();
32768 Roo.isTouch ? this.closable(false) : this.closable(true);
32770 this.fireEvent('refresh', this);
32773 onRemove : function(e, el, o)
32775 e.preventDefault();
32777 this.fireEvent('remove', this, o);
32781 remove : function(o)
32785 Roo.each(this.files, function(file){
32786 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32795 this.files = files;
32802 Roo.each(this.files, function(file){
32807 file.target.remove();
32816 onClick : function(e, el, o)
32818 e.preventDefault();
32820 this.fireEvent('click', this, o);
32824 closable : function(closable)
32826 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32828 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32840 xhrOnLoad : function(xhr)
32842 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32846 if (xhr.readyState !== 4) {
32848 this.fireEvent('exception', this, xhr);
32852 var response = Roo.decode(xhr.responseText);
32854 if(!response.success){
32856 this.fireEvent('exception', this, xhr);
32860 var file = this.renderPreview(response.data);
32862 this.files.push(file);
32866 this.fireEvent('afterupload', this, xhr);
32870 xhrOnError : function(xhr)
32872 Roo.log('xhr on error');
32874 var response = Roo.decode(xhr.responseText);
32881 process : function(file)
32883 if(this.fireEvent('process', this, file) !== false){
32884 if(this.editable && file.type.indexOf('image') != -1){
32885 this.fireEvent('edit', this, file);
32889 this.uploadStart(file, false);
32896 uploadStart : function(file, crop)
32898 this.xhr = new XMLHttpRequest();
32900 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32905 file.xhr = this.xhr;
32907 this.managerEl.createChild({
32909 cls : 'roo-document-manager-loading',
32913 tooltip : file.name,
32914 cls : 'roo-document-manager-thumb',
32915 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32921 this.xhr.open(this.method, this.url, true);
32924 "Accept": "application/json",
32925 "Cache-Control": "no-cache",
32926 "X-Requested-With": "XMLHttpRequest"
32929 for (var headerName in headers) {
32930 var headerValue = headers[headerName];
32932 this.xhr.setRequestHeader(headerName, headerValue);
32938 this.xhr.onload = function()
32940 _this.xhrOnLoad(_this.xhr);
32943 this.xhr.onerror = function()
32945 _this.xhrOnError(_this.xhr);
32948 var formData = new FormData();
32950 formData.append('returnHTML', 'NO');
32953 formData.append('crop', crop);
32956 formData.append(this.paramName, file, file.name);
32963 if(this.fireEvent('prepare', this, formData, options) != false){
32965 if(options.manually){
32969 this.xhr.send(formData);
32973 this.uploadCancel();
32976 uploadCancel : function()
32982 this.delegates = [];
32984 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32991 renderPreview : function(file)
32993 if(typeof(file.target) != 'undefined' && file.target){
32997 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32999 var previewEl = this.managerEl.createChild({
33001 cls : 'roo-document-manager-preview',
33005 tooltip : file[this.toolTipName],
33006 cls : 'roo-document-manager-thumb',
33007 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33012 html : '<i class="fa fa-times-circle"></i>'
33017 var close = previewEl.select('button.close', true).first();
33019 close.on('click', this.onRemove, this, file);
33021 file.target = previewEl;
33023 var image = previewEl.select('img', true).first();
33027 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33029 image.on('click', this.onClick, this, file);
33031 this.fireEvent('previewrendered', this, file);
33037 onPreviewLoad : function(file, image)
33039 if(typeof(file.target) == 'undefined' || !file.target){
33043 var width = image.dom.naturalWidth || image.dom.width;
33044 var height = image.dom.naturalHeight || image.dom.height;
33046 if(!this.previewResize) {
33050 if(width > height){
33051 file.target.addClass('wide');
33055 file.target.addClass('tall');
33060 uploadFromSource : function(file, crop)
33062 this.xhr = new XMLHttpRequest();
33064 this.managerEl.createChild({
33066 cls : 'roo-document-manager-loading',
33070 tooltip : file.name,
33071 cls : 'roo-document-manager-thumb',
33072 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33078 this.xhr.open(this.method, this.url, true);
33081 "Accept": "application/json",
33082 "Cache-Control": "no-cache",
33083 "X-Requested-With": "XMLHttpRequest"
33086 for (var headerName in headers) {
33087 var headerValue = headers[headerName];
33089 this.xhr.setRequestHeader(headerName, headerValue);
33095 this.xhr.onload = function()
33097 _this.xhrOnLoad(_this.xhr);
33100 this.xhr.onerror = function()
33102 _this.xhrOnError(_this.xhr);
33105 var formData = new FormData();
33107 formData.append('returnHTML', 'NO');
33109 formData.append('crop', crop);
33111 if(typeof(file.filename) != 'undefined'){
33112 formData.append('filename', file.filename);
33115 if(typeof(file.mimetype) != 'undefined'){
33116 formData.append('mimetype', file.mimetype);
33121 if(this.fireEvent('prepare', this, formData) != false){
33122 this.xhr.send(formData);
33132 * @class Roo.bootstrap.DocumentViewer
33133 * @extends Roo.bootstrap.Component
33134 * Bootstrap DocumentViewer class
33135 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33136 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33139 * Create a new DocumentViewer
33140 * @param {Object} config The config object
33143 Roo.bootstrap.DocumentViewer = function(config){
33144 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33149 * Fire after initEvent
33150 * @param {Roo.bootstrap.DocumentViewer} this
33156 * @param {Roo.bootstrap.DocumentViewer} this
33161 * Fire after download button
33162 * @param {Roo.bootstrap.DocumentViewer} this
33167 * Fire after trash button
33168 * @param {Roo.bootstrap.DocumentViewer} this
33175 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33177 showDownload : true,
33181 getAutoCreate : function()
33185 cls : 'roo-document-viewer',
33189 cls : 'roo-document-viewer-body',
33193 cls : 'roo-document-viewer-thumb',
33197 cls : 'roo-document-viewer-image'
33205 cls : 'roo-document-viewer-footer',
33208 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33212 cls : 'btn-group roo-document-viewer-download',
33216 cls : 'btn btn-default',
33217 html : '<i class="fa fa-download"></i>'
33223 cls : 'btn-group roo-document-viewer-trash',
33227 cls : 'btn btn-default',
33228 html : '<i class="fa fa-trash"></i>'
33241 initEvents : function()
33243 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33244 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33246 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33247 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33249 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33250 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33252 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33253 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33255 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33256 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33258 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33259 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33261 this.bodyEl.on('click', this.onClick, this);
33262 this.downloadBtn.on('click', this.onDownload, this);
33263 this.trashBtn.on('click', this.onTrash, this);
33265 this.downloadBtn.hide();
33266 this.trashBtn.hide();
33268 if(this.showDownload){
33269 this.downloadBtn.show();
33272 if(this.showTrash){
33273 this.trashBtn.show();
33276 if(!this.showDownload && !this.showTrash) {
33277 this.footerEl.hide();
33282 initial : function()
33284 this.fireEvent('initial', this);
33288 onClick : function(e)
33290 e.preventDefault();
33292 this.fireEvent('click', this);
33295 onDownload : function(e)
33297 e.preventDefault();
33299 this.fireEvent('download', this);
33302 onTrash : function(e)
33304 e.preventDefault();
33306 this.fireEvent('trash', this);
33318 * @class Roo.bootstrap.NavProgressBar
33319 * @extends Roo.bootstrap.Component
33320 * Bootstrap NavProgressBar class
33323 * Create a new nav progress bar
33324 * @param {Object} config The config object
33327 Roo.bootstrap.NavProgressBar = function(config){
33328 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33330 this.bullets = this.bullets || [];
33332 // Roo.bootstrap.NavProgressBar.register(this);
33336 * Fires when the active item changes
33337 * @param {Roo.bootstrap.NavProgressBar} this
33338 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33339 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33346 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33351 getAutoCreate : function()
33353 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33357 cls : 'roo-navigation-bar-group',
33361 cls : 'roo-navigation-top-bar'
33365 cls : 'roo-navigation-bullets-bar',
33369 cls : 'roo-navigation-bar'
33376 cls : 'roo-navigation-bottom-bar'
33386 initEvents: function()
33391 onRender : function(ct, position)
33393 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33395 if(this.bullets.length){
33396 Roo.each(this.bullets, function(b){
33405 addItem : function(cfg)
33407 var item = new Roo.bootstrap.NavProgressItem(cfg);
33409 item.parentId = this.id;
33410 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33413 var top = new Roo.bootstrap.Element({
33415 cls : 'roo-navigation-bar-text'
33418 var bottom = new Roo.bootstrap.Element({
33420 cls : 'roo-navigation-bar-text'
33423 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33424 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33426 var topText = new Roo.bootstrap.Element({
33428 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33431 var bottomText = new Roo.bootstrap.Element({
33433 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33436 topText.onRender(top.el, null);
33437 bottomText.onRender(bottom.el, null);
33440 item.bottomEl = bottom;
33443 this.barItems.push(item);
33448 getActive : function()
33450 var active = false;
33452 Roo.each(this.barItems, function(v){
33454 if (!v.isActive()) {
33466 setActiveItem : function(item)
33470 Roo.each(this.barItems, function(v){
33471 if (v.rid == item.rid) {
33475 if (v.isActive()) {
33476 v.setActive(false);
33481 item.setActive(true);
33483 this.fireEvent('changed', this, item, prev);
33486 getBarItem: function(rid)
33490 Roo.each(this.barItems, function(e) {
33491 if (e.rid != rid) {
33502 indexOfItem : function(item)
33506 Roo.each(this.barItems, function(v, i){
33508 if (v.rid != item.rid) {
33519 setActiveNext : function()
33521 var i = this.indexOfItem(this.getActive());
33523 if (i > this.barItems.length) {
33527 this.setActiveItem(this.barItems[i+1]);
33530 setActivePrev : function()
33532 var i = this.indexOfItem(this.getActive());
33538 this.setActiveItem(this.barItems[i-1]);
33541 format : function()
33543 if(!this.barItems.length){
33547 var width = 100 / this.barItems.length;
33549 Roo.each(this.barItems, function(i){
33550 i.el.setStyle('width', width + '%');
33551 i.topEl.el.setStyle('width', width + '%');
33552 i.bottomEl.el.setStyle('width', width + '%');
33561 * Nav Progress Item
33566 * @class Roo.bootstrap.NavProgressItem
33567 * @extends Roo.bootstrap.Component
33568 * Bootstrap NavProgressItem class
33569 * @cfg {String} rid the reference id
33570 * @cfg {Boolean} active (true|false) Is item active default false
33571 * @cfg {Boolean} disabled (true|false) Is item active default false
33572 * @cfg {String} html
33573 * @cfg {String} position (top|bottom) text position default bottom
33574 * @cfg {String} icon show icon instead of number
33577 * Create a new NavProgressItem
33578 * @param {Object} config The config object
33580 Roo.bootstrap.NavProgressItem = function(config){
33581 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33586 * The raw click event for the entire grid.
33587 * @param {Roo.bootstrap.NavProgressItem} this
33588 * @param {Roo.EventObject} e
33595 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33601 position : 'bottom',
33604 getAutoCreate : function()
33606 var iconCls = 'roo-navigation-bar-item-icon';
33608 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33612 cls: 'roo-navigation-bar-item',
33622 cfg.cls += ' active';
33625 cfg.cls += ' disabled';
33631 disable : function()
33633 this.setDisabled(true);
33636 enable : function()
33638 this.setDisabled(false);
33641 initEvents: function()
33643 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33645 this.iconEl.on('click', this.onClick, this);
33648 onClick : function(e)
33650 e.preventDefault();
33656 if(this.fireEvent('click', this, e) === false){
33660 this.parent().setActiveItem(this);
33663 isActive: function ()
33665 return this.active;
33668 setActive : function(state)
33670 if(this.active == state){
33674 this.active = state;
33677 this.el.addClass('active');
33681 this.el.removeClass('active');
33686 setDisabled : function(state)
33688 if(this.disabled == state){
33692 this.disabled = state;
33695 this.el.addClass('disabled');
33699 this.el.removeClass('disabled');
33702 tooltipEl : function()
33704 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33717 * @class Roo.bootstrap.FieldLabel
33718 * @extends Roo.bootstrap.Component
33719 * Bootstrap FieldLabel class
33720 * @cfg {String} html contents of the element
33721 * @cfg {String} tag tag of the element default label
33722 * @cfg {String} cls class of the element
33723 * @cfg {String} target label target
33724 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33725 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33726 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33727 * @cfg {String} iconTooltip default "This field is required"
33728 * @cfg {String} indicatorpos (left|right) default left
33731 * Create a new FieldLabel
33732 * @param {Object} config The config object
33735 Roo.bootstrap.FieldLabel = function(config){
33736 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33741 * Fires after the field has been marked as invalid.
33742 * @param {Roo.form.FieldLabel} this
33743 * @param {String} msg The validation message
33748 * Fires after the field has been validated with no errors.
33749 * @param {Roo.form.FieldLabel} this
33755 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33762 invalidClass : 'has-warning',
33763 validClass : 'has-success',
33764 iconTooltip : 'This field is required',
33765 indicatorpos : 'left',
33767 getAutoCreate : function(){
33770 if (!this.allowBlank) {
33776 cls : 'roo-bootstrap-field-label ' + this.cls,
33781 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33782 tooltip : this.iconTooltip
33791 if(this.indicatorpos == 'right'){
33794 cls : 'roo-bootstrap-field-label ' + this.cls,
33803 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33804 tooltip : this.iconTooltip
33813 initEvents: function()
33815 Roo.bootstrap.Element.superclass.initEvents.call(this);
33817 this.indicator = this.indicatorEl();
33819 if(this.indicator){
33820 this.indicator.removeClass('visible');
33821 this.indicator.addClass('invisible');
33824 Roo.bootstrap.FieldLabel.register(this);
33827 indicatorEl : function()
33829 var indicator = this.el.select('i.roo-required-indicator',true).first();
33840 * Mark this field as valid
33842 markValid : function()
33844 if(this.indicator){
33845 this.indicator.removeClass('visible');
33846 this.indicator.addClass('invisible');
33848 if (Roo.bootstrap.version == 3) {
33849 this.el.removeClass(this.invalidClass);
33850 this.el.addClass(this.validClass);
33852 this.el.removeClass('is-invalid');
33853 this.el.addClass('is-valid');
33857 this.fireEvent('valid', this);
33861 * Mark this field as invalid
33862 * @param {String} msg The validation message
33864 markInvalid : function(msg)
33866 if(this.indicator){
33867 this.indicator.removeClass('invisible');
33868 this.indicator.addClass('visible');
33870 if (Roo.bootstrap.version == 3) {
33871 this.el.removeClass(this.validClass);
33872 this.el.addClass(this.invalidClass);
33874 this.el.removeClass('is-valid');
33875 this.el.addClass('is-invalid');
33879 this.fireEvent('invalid', this, msg);
33885 Roo.apply(Roo.bootstrap.FieldLabel, {
33890 * register a FieldLabel Group
33891 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33893 register : function(label)
33895 if(this.groups.hasOwnProperty(label.target)){
33899 this.groups[label.target] = label;
33903 * fetch a FieldLabel Group based on the target
33904 * @param {string} target
33905 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33907 get: function(target) {
33908 if (typeof(this.groups[target]) == 'undefined') {
33912 return this.groups[target] ;
33921 * page DateSplitField.
33927 * @class Roo.bootstrap.DateSplitField
33928 * @extends Roo.bootstrap.Component
33929 * Bootstrap DateSplitField class
33930 * @cfg {string} fieldLabel - the label associated
33931 * @cfg {Number} labelWidth set the width of label (0-12)
33932 * @cfg {String} labelAlign (top|left)
33933 * @cfg {Boolean} dayAllowBlank (true|false) default false
33934 * @cfg {Boolean} monthAllowBlank (true|false) default false
33935 * @cfg {Boolean} yearAllowBlank (true|false) default false
33936 * @cfg {string} dayPlaceholder
33937 * @cfg {string} monthPlaceholder
33938 * @cfg {string} yearPlaceholder
33939 * @cfg {string} dayFormat default 'd'
33940 * @cfg {string} monthFormat default 'm'
33941 * @cfg {string} yearFormat default 'Y'
33942 * @cfg {Number} labellg set the width of label (1-12)
33943 * @cfg {Number} labelmd set the width of label (1-12)
33944 * @cfg {Number} labelsm set the width of label (1-12)
33945 * @cfg {Number} labelxs set the width of label (1-12)
33949 * Create a new DateSplitField
33950 * @param {Object} config The config object
33953 Roo.bootstrap.DateSplitField = function(config){
33954 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33960 * getting the data of years
33961 * @param {Roo.bootstrap.DateSplitField} this
33962 * @param {Object} years
33967 * getting the data of days
33968 * @param {Roo.bootstrap.DateSplitField} this
33969 * @param {Object} days
33974 * Fires after the field has been marked as invalid.
33975 * @param {Roo.form.Field} this
33976 * @param {String} msg The validation message
33981 * Fires after the field has been validated with no errors.
33982 * @param {Roo.form.Field} this
33988 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33991 labelAlign : 'top',
33993 dayAllowBlank : false,
33994 monthAllowBlank : false,
33995 yearAllowBlank : false,
33996 dayPlaceholder : '',
33997 monthPlaceholder : '',
33998 yearPlaceholder : '',
34002 isFormField : true,
34008 getAutoCreate : function()
34012 cls : 'row roo-date-split-field-group',
34017 cls : 'form-hidden-field roo-date-split-field-group-value',
34023 var labelCls = 'col-md-12';
34024 var contentCls = 'col-md-4';
34026 if(this.fieldLabel){
34030 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34034 html : this.fieldLabel
34039 if(this.labelAlign == 'left'){
34041 if(this.labelWidth > 12){
34042 label.style = "width: " + this.labelWidth + 'px';
34045 if(this.labelWidth < 13 && this.labelmd == 0){
34046 this.labelmd = this.labelWidth;
34049 if(this.labellg > 0){
34050 labelCls = ' col-lg-' + this.labellg;
34051 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34054 if(this.labelmd > 0){
34055 labelCls = ' col-md-' + this.labelmd;
34056 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34059 if(this.labelsm > 0){
34060 labelCls = ' col-sm-' + this.labelsm;
34061 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34064 if(this.labelxs > 0){
34065 labelCls = ' col-xs-' + this.labelxs;
34066 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34070 label.cls += ' ' + labelCls;
34072 cfg.cn.push(label);
34075 Roo.each(['day', 'month', 'year'], function(t){
34078 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34085 inputEl: function ()
34087 return this.el.select('.roo-date-split-field-group-value', true).first();
34090 onRender : function(ct, position)
34094 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34096 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34098 this.dayField = new Roo.bootstrap.ComboBox({
34099 allowBlank : this.dayAllowBlank,
34100 alwaysQuery : true,
34101 displayField : 'value',
34104 forceSelection : true,
34106 placeholder : this.dayPlaceholder,
34107 selectOnFocus : true,
34108 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34109 triggerAction : 'all',
34111 valueField : 'value',
34112 store : new Roo.data.SimpleStore({
34113 data : (function() {
34115 _this.fireEvent('days', _this, days);
34118 fields : [ 'value' ]
34121 select : function (_self, record, index)
34123 _this.setValue(_this.getValue());
34128 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34130 this.monthField = new Roo.bootstrap.MonthField({
34131 after : '<i class=\"fa fa-calendar\"></i>',
34132 allowBlank : this.monthAllowBlank,
34133 placeholder : this.monthPlaceholder,
34136 render : function (_self)
34138 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34139 e.preventDefault();
34143 select : function (_self, oldvalue, newvalue)
34145 _this.setValue(_this.getValue());
34150 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34152 this.yearField = new Roo.bootstrap.ComboBox({
34153 allowBlank : this.yearAllowBlank,
34154 alwaysQuery : true,
34155 displayField : 'value',
34158 forceSelection : true,
34160 placeholder : this.yearPlaceholder,
34161 selectOnFocus : true,
34162 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34163 triggerAction : 'all',
34165 valueField : 'value',
34166 store : new Roo.data.SimpleStore({
34167 data : (function() {
34169 _this.fireEvent('years', _this, years);
34172 fields : [ 'value' ]
34175 select : function (_self, record, index)
34177 _this.setValue(_this.getValue());
34182 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34185 setValue : function(v, format)
34187 this.inputEl.dom.value = v;
34189 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34191 var d = Date.parseDate(v, f);
34198 this.setDay(d.format(this.dayFormat));
34199 this.setMonth(d.format(this.monthFormat));
34200 this.setYear(d.format(this.yearFormat));
34207 setDay : function(v)
34209 this.dayField.setValue(v);
34210 this.inputEl.dom.value = this.getValue();
34215 setMonth : function(v)
34217 this.monthField.setValue(v, true);
34218 this.inputEl.dom.value = this.getValue();
34223 setYear : function(v)
34225 this.yearField.setValue(v);
34226 this.inputEl.dom.value = this.getValue();
34231 getDay : function()
34233 return this.dayField.getValue();
34236 getMonth : function()
34238 return this.monthField.getValue();
34241 getYear : function()
34243 return this.yearField.getValue();
34246 getValue : function()
34248 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34250 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34260 this.inputEl.dom.value = '';
34265 validate : function()
34267 var d = this.dayField.validate();
34268 var m = this.monthField.validate();
34269 var y = this.yearField.validate();
34274 (!this.dayAllowBlank && !d) ||
34275 (!this.monthAllowBlank && !m) ||
34276 (!this.yearAllowBlank && !y)
34281 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34290 this.markInvalid();
34295 markValid : function()
34298 var label = this.el.select('label', true).first();
34299 var icon = this.el.select('i.fa-star', true).first();
34305 this.fireEvent('valid', this);
34309 * Mark this field as invalid
34310 * @param {String} msg The validation message
34312 markInvalid : function(msg)
34315 var label = this.el.select('label', true).first();
34316 var icon = this.el.select('i.fa-star', true).first();
34318 if(label && !icon){
34319 this.el.select('.roo-date-split-field-label', true).createChild({
34321 cls : 'text-danger fa fa-lg fa-star',
34322 tooltip : 'This field is required',
34323 style : 'margin-right:5px;'
34327 this.fireEvent('invalid', this, msg);
34330 clearInvalid : function()
34332 var label = this.el.select('label', true).first();
34333 var icon = this.el.select('i.fa-star', true).first();
34339 this.fireEvent('valid', this);
34342 getName: function()
34352 * http://masonry.desandro.com
34354 * The idea is to render all the bricks based on vertical width...
34356 * The original code extends 'outlayer' - we might need to use that....
34362 * @class Roo.bootstrap.LayoutMasonry
34363 * @extends Roo.bootstrap.Component
34364 * Bootstrap Layout Masonry class
34367 * Create a new Element
34368 * @param {Object} config The config object
34371 Roo.bootstrap.LayoutMasonry = function(config){
34373 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34377 Roo.bootstrap.LayoutMasonry.register(this);
34383 * Fire after layout the items
34384 * @param {Roo.bootstrap.LayoutMasonry} this
34385 * @param {Roo.EventObject} e
34392 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34395 * @cfg {Boolean} isLayoutInstant = no animation?
34397 isLayoutInstant : false, // needed?
34400 * @cfg {Number} boxWidth width of the columns
34405 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34410 * @cfg {Number} padWidth padding below box..
34415 * @cfg {Number} gutter gutter width..
34420 * @cfg {Number} maxCols maximum number of columns
34426 * @cfg {Boolean} isAutoInitial defalut true
34428 isAutoInitial : true,
34433 * @cfg {Boolean} isHorizontal defalut false
34435 isHorizontal : false,
34437 currentSize : null,
34443 bricks: null, //CompositeElement
34447 _isLayoutInited : false,
34449 // isAlternative : false, // only use for vertical layout...
34452 * @cfg {Number} alternativePadWidth padding below box..
34454 alternativePadWidth : 50,
34456 selectedBrick : [],
34458 getAutoCreate : function(){
34460 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34464 cls: 'blog-masonary-wrapper ' + this.cls,
34466 cls : 'mas-boxes masonary'
34473 getChildContainer: function( )
34475 if (this.boxesEl) {
34476 return this.boxesEl;
34479 this.boxesEl = this.el.select('.mas-boxes').first();
34481 return this.boxesEl;
34485 initEvents : function()
34489 if(this.isAutoInitial){
34490 Roo.log('hook children rendered');
34491 this.on('childrenrendered', function() {
34492 Roo.log('children rendered');
34498 initial : function()
34500 this.selectedBrick = [];
34502 this.currentSize = this.el.getBox(true);
34504 Roo.EventManager.onWindowResize(this.resize, this);
34506 if(!this.isAutoInitial){
34514 //this.layout.defer(500,this);
34518 resize : function()
34520 var cs = this.el.getBox(true);
34523 this.currentSize.width == cs.width &&
34524 this.currentSize.x == cs.x &&
34525 this.currentSize.height == cs.height &&
34526 this.currentSize.y == cs.y
34528 Roo.log("no change in with or X or Y");
34532 this.currentSize = cs;
34538 layout : function()
34540 this._resetLayout();
34542 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34544 this.layoutItems( isInstant );
34546 this._isLayoutInited = true;
34548 this.fireEvent('layout', this);
34552 _resetLayout : function()
34554 if(this.isHorizontal){
34555 this.horizontalMeasureColumns();
34559 this.verticalMeasureColumns();
34563 verticalMeasureColumns : function()
34565 this.getContainerWidth();
34567 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34568 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34572 var boxWidth = this.boxWidth + this.padWidth;
34574 if(this.containerWidth < this.boxWidth){
34575 boxWidth = this.containerWidth
34578 var containerWidth = this.containerWidth;
34580 var cols = Math.floor(containerWidth / boxWidth);
34582 this.cols = Math.max( cols, 1 );
34584 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34586 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34588 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34590 this.colWidth = boxWidth + avail - this.padWidth;
34592 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34593 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34596 horizontalMeasureColumns : function()
34598 this.getContainerWidth();
34600 var boxWidth = this.boxWidth;
34602 if(this.containerWidth < boxWidth){
34603 boxWidth = this.containerWidth;
34606 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34608 this.el.setHeight(boxWidth);
34612 getContainerWidth : function()
34614 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34617 layoutItems : function( isInstant )
34619 Roo.log(this.bricks);
34621 var items = Roo.apply([], this.bricks);
34623 if(this.isHorizontal){
34624 this._horizontalLayoutItems( items , isInstant );
34628 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34629 // this._verticalAlternativeLayoutItems( items , isInstant );
34633 this._verticalLayoutItems( items , isInstant );
34637 _verticalLayoutItems : function ( items , isInstant)
34639 if ( !items || !items.length ) {
34644 ['xs', 'xs', 'xs', 'tall'],
34645 ['xs', 'xs', 'tall'],
34646 ['xs', 'xs', 'sm'],
34647 ['xs', 'xs', 'xs'],
34653 ['sm', 'xs', 'xs'],
34657 ['tall', 'xs', 'xs', 'xs'],
34658 ['tall', 'xs', 'xs'],
34670 Roo.each(items, function(item, k){
34672 switch (item.size) {
34673 // these layouts take up a full box,
34684 boxes.push([item]);
34707 var filterPattern = function(box, length)
34715 var pattern = box.slice(0, length);
34719 Roo.each(pattern, function(i){
34720 format.push(i.size);
34723 Roo.each(standard, function(s){
34725 if(String(s) != String(format)){
34734 if(!match && length == 1){
34739 filterPattern(box, length - 1);
34743 queue.push(pattern);
34745 box = box.slice(length, box.length);
34747 filterPattern(box, 4);
34753 Roo.each(boxes, function(box, k){
34759 if(box.length == 1){
34764 filterPattern(box, 4);
34768 this._processVerticalLayoutQueue( queue, isInstant );
34772 // _verticalAlternativeLayoutItems : function( items , isInstant )
34774 // if ( !items || !items.length ) {
34778 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34782 _horizontalLayoutItems : function ( items , isInstant)
34784 if ( !items || !items.length || items.length < 3) {
34790 var eItems = items.slice(0, 3);
34792 items = items.slice(3, items.length);
34795 ['xs', 'xs', 'xs', 'wide'],
34796 ['xs', 'xs', 'wide'],
34797 ['xs', 'xs', 'sm'],
34798 ['xs', 'xs', 'xs'],
34804 ['sm', 'xs', 'xs'],
34808 ['wide', 'xs', 'xs', 'xs'],
34809 ['wide', 'xs', 'xs'],
34822 Roo.each(items, function(item, k){
34824 switch (item.size) {
34835 boxes.push([item]);
34859 var filterPattern = function(box, length)
34867 var pattern = box.slice(0, length);
34871 Roo.each(pattern, function(i){
34872 format.push(i.size);
34875 Roo.each(standard, function(s){
34877 if(String(s) != String(format)){
34886 if(!match && length == 1){
34891 filterPattern(box, length - 1);
34895 queue.push(pattern);
34897 box = box.slice(length, box.length);
34899 filterPattern(box, 4);
34905 Roo.each(boxes, function(box, k){
34911 if(box.length == 1){
34916 filterPattern(box, 4);
34923 var pos = this.el.getBox(true);
34927 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34929 var hit_end = false;
34931 Roo.each(queue, function(box){
34935 Roo.each(box, function(b){
34937 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34947 Roo.each(box, function(b){
34949 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34952 mx = Math.max(mx, b.x);
34956 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34960 Roo.each(box, function(b){
34962 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34976 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34979 /** Sets position of item in DOM
34980 * @param {Element} item
34981 * @param {Number} x - horizontal position
34982 * @param {Number} y - vertical position
34983 * @param {Boolean} isInstant - disables transitions
34985 _processVerticalLayoutQueue : function( queue, isInstant )
34987 var pos = this.el.getBox(true);
34992 for (var i = 0; i < this.cols; i++){
34996 Roo.each(queue, function(box, k){
34998 var col = k % this.cols;
35000 Roo.each(box, function(b,kk){
35002 b.el.position('absolute');
35004 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35005 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35007 if(b.size == 'md-left' || b.size == 'md-right'){
35008 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35009 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35012 b.el.setWidth(width);
35013 b.el.setHeight(height);
35015 b.el.select('iframe',true).setSize(width,height);
35019 for (var i = 0; i < this.cols; i++){
35021 if(maxY[i] < maxY[col]){
35026 col = Math.min(col, i);
35030 x = pos.x + col * (this.colWidth + this.padWidth);
35034 var positions = [];
35036 switch (box.length){
35038 positions = this.getVerticalOneBoxColPositions(x, y, box);
35041 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35044 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35047 positions = this.getVerticalFourBoxColPositions(x, y, box);
35053 Roo.each(box, function(b,kk){
35055 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35057 var sz = b.el.getSize();
35059 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35067 for (var i = 0; i < this.cols; i++){
35068 mY = Math.max(mY, maxY[i]);
35071 this.el.setHeight(mY - pos.y);
35075 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35077 // var pos = this.el.getBox(true);
35080 // var maxX = pos.right;
35082 // var maxHeight = 0;
35084 // Roo.each(items, function(item, k){
35088 // item.el.position('absolute');
35090 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35092 // item.el.setWidth(width);
35094 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35096 // item.el.setHeight(height);
35099 // item.el.setXY([x, y], isInstant ? false : true);
35101 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35104 // y = y + height + this.alternativePadWidth;
35106 // maxHeight = maxHeight + height + this.alternativePadWidth;
35110 // this.el.setHeight(maxHeight);
35114 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35116 var pos = this.el.getBox(true);
35121 var maxX = pos.right;
35123 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35125 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35127 Roo.each(queue, function(box, k){
35129 Roo.each(box, function(b, kk){
35131 b.el.position('absolute');
35133 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35134 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35136 if(b.size == 'md-left' || b.size == 'md-right'){
35137 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35138 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35141 b.el.setWidth(width);
35142 b.el.setHeight(height);
35150 var positions = [];
35152 switch (box.length){
35154 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35157 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35160 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35163 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35169 Roo.each(box, function(b,kk){
35171 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35173 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35181 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35183 Roo.each(eItems, function(b,k){
35185 b.size = (k == 0) ? 'sm' : 'xs';
35186 b.x = (k == 0) ? 2 : 1;
35187 b.y = (k == 0) ? 2 : 1;
35189 b.el.position('absolute');
35191 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35193 b.el.setWidth(width);
35195 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35197 b.el.setHeight(height);
35201 var positions = [];
35204 x : maxX - this.unitWidth * 2 - this.gutter,
35209 x : maxX - this.unitWidth,
35210 y : minY + (this.unitWidth + this.gutter) * 2
35214 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35218 Roo.each(eItems, function(b,k){
35220 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35226 getVerticalOneBoxColPositions : function(x, y, box)
35230 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35232 if(box[0].size == 'md-left'){
35236 if(box[0].size == 'md-right'){
35241 x : x + (this.unitWidth + this.gutter) * rand,
35248 getVerticalTwoBoxColPositions : function(x, y, box)
35252 if(box[0].size == 'xs'){
35256 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35260 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35274 x : x + (this.unitWidth + this.gutter) * 2,
35275 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35282 getVerticalThreeBoxColPositions : function(x, y, box)
35286 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35294 x : x + (this.unitWidth + this.gutter) * 1,
35299 x : x + (this.unitWidth + this.gutter) * 2,
35307 if(box[0].size == 'xs' && box[1].size == 'xs'){
35316 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35320 x : x + (this.unitWidth + this.gutter) * 1,
35334 x : x + (this.unitWidth + this.gutter) * 2,
35339 x : x + (this.unitWidth + this.gutter) * 2,
35340 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35347 getVerticalFourBoxColPositions : function(x, y, box)
35351 if(box[0].size == 'xs'){
35360 y : y + (this.unitHeight + this.gutter) * 1
35365 y : y + (this.unitHeight + this.gutter) * 2
35369 x : x + (this.unitWidth + this.gutter) * 1,
35383 x : x + (this.unitWidth + this.gutter) * 2,
35388 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35389 y : y + (this.unitHeight + this.gutter) * 1
35393 x : x + (this.unitWidth + this.gutter) * 2,
35394 y : y + (this.unitWidth + this.gutter) * 2
35401 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35405 if(box[0].size == 'md-left'){
35407 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35414 if(box[0].size == 'md-right'){
35416 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35417 y : minY + (this.unitWidth + this.gutter) * 1
35423 var rand = Math.floor(Math.random() * (4 - box[0].y));
35426 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35427 y : minY + (this.unitWidth + this.gutter) * rand
35434 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35438 if(box[0].size == 'xs'){
35441 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35446 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35447 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
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) * 2
35468 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35472 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35475 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35480 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35481 y : minY + (this.unitWidth + this.gutter) * 1
35485 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35486 y : minY + (this.unitWidth + this.gutter) * 2
35493 if(box[0].size == 'xs' && box[1].size == 'xs'){
35496 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35501 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35506 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35507 y : minY + (this.unitWidth + this.gutter) * 1
35515 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35520 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35521 y : minY + (this.unitWidth + this.gutter) * 2
35525 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35526 y : minY + (this.unitWidth + this.gutter) * 2
35533 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35537 if(box[0].size == 'xs'){
35540 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35545 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35550 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),
35555 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35556 y : minY + (this.unitWidth + this.gutter) * 1
35564 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35569 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35570 y : minY + (this.unitWidth + this.gutter) * 2
35574 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35575 y : minY + (this.unitWidth + this.gutter) * 2
35579 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),
35580 y : minY + (this.unitWidth + this.gutter) * 2
35588 * remove a Masonry Brick
35589 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35591 removeBrick : function(brick_id)
35597 for (var i = 0; i<this.bricks.length; i++) {
35598 if (this.bricks[i].id == brick_id) {
35599 this.bricks.splice(i,1);
35600 this.el.dom.removeChild(Roo.get(brick_id).dom);
35607 * adds a Masonry Brick
35608 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35610 addBrick : function(cfg)
35612 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35613 //this.register(cn);
35614 cn.parentId = this.id;
35615 cn.render(this.el);
35620 * register a Masonry Brick
35621 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35624 register : function(brick)
35626 this.bricks.push(brick);
35627 brick.masonryId = this.id;
35631 * clear all the Masonry Brick
35633 clearAll : function()
35636 //this.getChildContainer().dom.innerHTML = "";
35637 this.el.dom.innerHTML = '';
35640 getSelected : function()
35642 if (!this.selectedBrick) {
35646 return this.selectedBrick;
35650 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35654 * register a Masonry Layout
35655 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35658 register : function(layout)
35660 this.groups[layout.id] = layout;
35663 * fetch a Masonry Layout based on the masonry layout ID
35664 * @param {string} the masonry layout to add
35665 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35668 get: function(layout_id) {
35669 if (typeof(this.groups[layout_id]) == 'undefined') {
35672 return this.groups[layout_id] ;
35684 * http://masonry.desandro.com
35686 * The idea is to render all the bricks based on vertical width...
35688 * The original code extends 'outlayer' - we might need to use that....
35694 * @class Roo.bootstrap.LayoutMasonryAuto
35695 * @extends Roo.bootstrap.Component
35696 * Bootstrap Layout Masonry class
35699 * Create a new Element
35700 * @param {Object} config The config object
35703 Roo.bootstrap.LayoutMasonryAuto = function(config){
35704 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35707 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35710 * @cfg {Boolean} isFitWidth - resize the width..
35712 isFitWidth : false, // options..
35714 * @cfg {Boolean} isOriginLeft = left align?
35716 isOriginLeft : true,
35718 * @cfg {Boolean} isOriginTop = top align?
35720 isOriginTop : false,
35722 * @cfg {Boolean} isLayoutInstant = no animation?
35724 isLayoutInstant : false, // needed?
35726 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35728 isResizingContainer : true,
35730 * @cfg {Number} columnWidth width of the columns
35736 * @cfg {Number} maxCols maximum number of columns
35741 * @cfg {Number} padHeight padding below box..
35747 * @cfg {Boolean} isAutoInitial defalut true
35750 isAutoInitial : true,
35756 initialColumnWidth : 0,
35757 currentSize : null,
35759 colYs : null, // array.
35766 bricks: null, //CompositeElement
35767 cols : 0, // array?
35768 // element : null, // wrapped now this.el
35769 _isLayoutInited : null,
35772 getAutoCreate : function(){
35776 cls: 'blog-masonary-wrapper ' + this.cls,
35778 cls : 'mas-boxes masonary'
35785 getChildContainer: function( )
35787 if (this.boxesEl) {
35788 return this.boxesEl;
35791 this.boxesEl = this.el.select('.mas-boxes').first();
35793 return this.boxesEl;
35797 initEvents : function()
35801 if(this.isAutoInitial){
35802 Roo.log('hook children rendered');
35803 this.on('childrenrendered', function() {
35804 Roo.log('children rendered');
35811 initial : function()
35813 this.reloadItems();
35815 this.currentSize = this.el.getBox(true);
35817 /// was window resize... - let's see if this works..
35818 Roo.EventManager.onWindowResize(this.resize, this);
35820 if(!this.isAutoInitial){
35825 this.layout.defer(500,this);
35828 reloadItems: function()
35830 this.bricks = this.el.select('.masonry-brick', true);
35832 this.bricks.each(function(b) {
35833 //Roo.log(b.getSize());
35834 if (!b.attr('originalwidth')) {
35835 b.attr('originalwidth', b.getSize().width);
35840 Roo.log(this.bricks.elements.length);
35843 resize : function()
35846 var cs = this.el.getBox(true);
35848 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35849 Roo.log("no change in with or X");
35852 this.currentSize = cs;
35856 layout : function()
35859 this._resetLayout();
35860 //this._manageStamps();
35862 // don't animate first layout
35863 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35864 this.layoutItems( isInstant );
35866 // flag for initalized
35867 this._isLayoutInited = true;
35870 layoutItems : function( isInstant )
35872 //var items = this._getItemsForLayout( this.items );
35873 // original code supports filtering layout items.. we just ignore it..
35875 this._layoutItems( this.bricks , isInstant );
35877 this._postLayout();
35879 _layoutItems : function ( items , isInstant)
35881 //this.fireEvent( 'layout', this, items );
35884 if ( !items || !items.elements.length ) {
35885 // no items, emit event with empty array
35890 items.each(function(item) {
35891 Roo.log("layout item");
35893 // get x/y object from method
35894 var position = this._getItemLayoutPosition( item );
35896 position.item = item;
35897 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35898 queue.push( position );
35901 this._processLayoutQueue( queue );
35903 /** Sets position of item in DOM
35904 * @param {Element} item
35905 * @param {Number} x - horizontal position
35906 * @param {Number} y - vertical position
35907 * @param {Boolean} isInstant - disables transitions
35909 _processLayoutQueue : function( queue )
35911 for ( var i=0, len = queue.length; i < len; i++ ) {
35912 var obj = queue[i];
35913 obj.item.position('absolute');
35914 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35920 * Any logic you want to do after each layout,
35921 * i.e. size the container
35923 _postLayout : function()
35925 this.resizeContainer();
35928 resizeContainer : function()
35930 if ( !this.isResizingContainer ) {
35933 var size = this._getContainerSize();
35935 this.el.setSize(size.width,size.height);
35936 this.boxesEl.setSize(size.width,size.height);
35942 _resetLayout : function()
35944 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35945 this.colWidth = this.el.getWidth();
35946 //this.gutter = this.el.getWidth();
35948 this.measureColumns();
35954 this.colYs.push( 0 );
35960 measureColumns : function()
35962 this.getContainerWidth();
35963 // if columnWidth is 0, default to outerWidth of first item
35964 if ( !this.columnWidth ) {
35965 var firstItem = this.bricks.first();
35966 Roo.log(firstItem);
35967 this.columnWidth = this.containerWidth;
35968 if (firstItem && firstItem.attr('originalwidth') ) {
35969 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35971 // columnWidth fall back to item of first element
35972 Roo.log("set column width?");
35973 this.initialColumnWidth = this.columnWidth ;
35975 // if first elem has no width, default to size of container
35980 if (this.initialColumnWidth) {
35981 this.columnWidth = this.initialColumnWidth;
35986 // column width is fixed at the top - however if container width get's smaller we should
35989 // this bit calcs how man columns..
35991 var columnWidth = this.columnWidth += this.gutter;
35993 // calculate columns
35994 var containerWidth = this.containerWidth + this.gutter;
35996 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35997 // fix rounding errors, typically with gutters
35998 var excess = columnWidth - containerWidth % columnWidth;
36001 // if overshoot is less than a pixel, round up, otherwise floor it
36002 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36003 cols = Math[ mathMethod ]( cols );
36004 this.cols = Math.max( cols, 1 );
36005 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36007 // padding positioning..
36008 var totalColWidth = this.cols * this.columnWidth;
36009 var padavail = this.containerWidth - totalColWidth;
36010 // so for 2 columns - we need 3 'pads'
36012 var padNeeded = (1+this.cols) * this.padWidth;
36014 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36016 this.columnWidth += padExtra
36017 //this.padWidth = Math.floor(padavail / ( this.cols));
36019 // adjust colum width so that padding is fixed??
36021 // we have 3 columns ... total = width * 3
36022 // we have X left over... that should be used by
36024 //if (this.expandC) {
36032 getContainerWidth : function()
36034 /* // container is parent if fit width
36035 var container = this.isFitWidth ? this.element.parentNode : this.element;
36036 // check that this.size and size are there
36037 // IE8 triggers resize on body size change, so they might not be
36039 var size = getSize( container ); //FIXME
36040 this.containerWidth = size && size.innerWidth; //FIXME
36043 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36047 _getItemLayoutPosition : function( item ) // what is item?
36049 // we resize the item to our columnWidth..
36051 item.setWidth(this.columnWidth);
36052 item.autoBoxAdjust = false;
36054 var sz = item.getSize();
36056 // how many columns does this brick span
36057 var remainder = this.containerWidth % this.columnWidth;
36059 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36060 // round if off by 1 pixel, otherwise use ceil
36061 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36062 colSpan = Math.min( colSpan, this.cols );
36064 // normally this should be '1' as we dont' currently allow multi width columns..
36066 var colGroup = this._getColGroup( colSpan );
36067 // get the minimum Y value from the columns
36068 var minimumY = Math.min.apply( Math, colGroup );
36069 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36071 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36073 // position the brick
36075 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36076 y: this.currentSize.y + minimumY + this.padHeight
36080 // apply setHeight to necessary columns
36081 var setHeight = minimumY + sz.height + this.padHeight;
36082 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36084 var setSpan = this.cols + 1 - colGroup.length;
36085 for ( var i = 0; i < setSpan; i++ ) {
36086 this.colYs[ shortColIndex + i ] = setHeight ;
36093 * @param {Number} colSpan - number of columns the element spans
36094 * @returns {Array} colGroup
36096 _getColGroup : function( colSpan )
36098 if ( colSpan < 2 ) {
36099 // if brick spans only one column, use all the column Ys
36104 // how many different places could this brick fit horizontally
36105 var groupCount = this.cols + 1 - colSpan;
36106 // for each group potential horizontal position
36107 for ( var i = 0; i < groupCount; i++ ) {
36108 // make an array of colY values for that one group
36109 var groupColYs = this.colYs.slice( i, i + colSpan );
36110 // and get the max value of the array
36111 colGroup[i] = Math.max.apply( Math, groupColYs );
36116 _manageStamp : function( stamp )
36118 var stampSize = stamp.getSize();
36119 var offset = stamp.getBox();
36120 // get the columns that this stamp affects
36121 var firstX = this.isOriginLeft ? offset.x : offset.right;
36122 var lastX = firstX + stampSize.width;
36123 var firstCol = Math.floor( firstX / this.columnWidth );
36124 firstCol = Math.max( 0, firstCol );
36126 var lastCol = Math.floor( lastX / this.columnWidth );
36127 // lastCol should not go over if multiple of columnWidth #425
36128 lastCol -= lastX % this.columnWidth ? 0 : 1;
36129 lastCol = Math.min( this.cols - 1, lastCol );
36131 // set colYs to bottom of the stamp
36132 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36135 for ( var i = firstCol; i <= lastCol; i++ ) {
36136 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36141 _getContainerSize : function()
36143 this.maxY = Math.max.apply( Math, this.colYs );
36148 if ( this.isFitWidth ) {
36149 size.width = this._getContainerFitWidth();
36155 _getContainerFitWidth : function()
36157 var unusedCols = 0;
36158 // count unused columns
36161 if ( this.colYs[i] !== 0 ) {
36166 // fit container to columns that have been used
36167 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36170 needsResizeLayout : function()
36172 var previousWidth = this.containerWidth;
36173 this.getContainerWidth();
36174 return previousWidth !== this.containerWidth;
36189 * @class Roo.bootstrap.MasonryBrick
36190 * @extends Roo.bootstrap.Component
36191 * Bootstrap MasonryBrick class
36194 * Create a new MasonryBrick
36195 * @param {Object} config The config object
36198 Roo.bootstrap.MasonryBrick = function(config){
36200 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36202 Roo.bootstrap.MasonryBrick.register(this);
36208 * When a MasonryBrick is clcik
36209 * @param {Roo.bootstrap.MasonryBrick} this
36210 * @param {Roo.EventObject} e
36216 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36219 * @cfg {String} title
36223 * @cfg {String} html
36227 * @cfg {String} bgimage
36231 * @cfg {String} videourl
36235 * @cfg {String} cls
36239 * @cfg {String} href
36243 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36248 * @cfg {String} placetitle (center|bottom)
36253 * @cfg {Boolean} isFitContainer defalut true
36255 isFitContainer : true,
36258 * @cfg {Boolean} preventDefault defalut false
36260 preventDefault : false,
36263 * @cfg {Boolean} inverse defalut false
36265 maskInverse : false,
36267 getAutoCreate : function()
36269 if(!this.isFitContainer){
36270 return this.getSplitAutoCreate();
36273 var cls = 'masonry-brick masonry-brick-full';
36275 if(this.href.length){
36276 cls += ' masonry-brick-link';
36279 if(this.bgimage.length){
36280 cls += ' masonry-brick-image';
36283 if(this.maskInverse){
36284 cls += ' mask-inverse';
36287 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36288 cls += ' enable-mask';
36292 cls += ' masonry-' + this.size + '-brick';
36295 if(this.placetitle.length){
36297 switch (this.placetitle) {
36299 cls += ' masonry-center-title';
36302 cls += ' masonry-bottom-title';
36309 if(!this.html.length && !this.bgimage.length){
36310 cls += ' masonry-center-title';
36313 if(!this.html.length && this.bgimage.length){
36314 cls += ' masonry-bottom-title';
36319 cls += ' ' + this.cls;
36323 tag: (this.href.length) ? 'a' : 'div',
36328 cls: 'masonry-brick-mask'
36332 cls: 'masonry-brick-paragraph',
36338 if(this.href.length){
36339 cfg.href = this.href;
36342 var cn = cfg.cn[1].cn;
36344 if(this.title.length){
36347 cls: 'masonry-brick-title',
36352 if(this.html.length){
36355 cls: 'masonry-brick-text',
36360 if (!this.title.length && !this.html.length) {
36361 cfg.cn[1].cls += ' hide';
36364 if(this.bgimage.length){
36367 cls: 'masonry-brick-image-view',
36372 if(this.videourl.length){
36373 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36374 // youtube support only?
36377 cls: 'masonry-brick-image-view',
36380 allowfullscreen : true
36388 getSplitAutoCreate : function()
36390 var cls = 'masonry-brick masonry-brick-split';
36392 if(this.href.length){
36393 cls += ' masonry-brick-link';
36396 if(this.bgimage.length){
36397 cls += ' masonry-brick-image';
36401 cls += ' masonry-' + this.size + '-brick';
36404 switch (this.placetitle) {
36406 cls += ' masonry-center-title';
36409 cls += ' masonry-bottom-title';
36412 if(!this.bgimage.length){
36413 cls += ' masonry-center-title';
36416 if(this.bgimage.length){
36417 cls += ' masonry-bottom-title';
36423 cls += ' ' + this.cls;
36427 tag: (this.href.length) ? 'a' : 'div',
36432 cls: 'masonry-brick-split-head',
36436 cls: 'masonry-brick-paragraph',
36443 cls: 'masonry-brick-split-body',
36449 if(this.href.length){
36450 cfg.href = this.href;
36453 if(this.title.length){
36454 cfg.cn[0].cn[0].cn.push({
36456 cls: 'masonry-brick-title',
36461 if(this.html.length){
36462 cfg.cn[1].cn.push({
36464 cls: 'masonry-brick-text',
36469 if(this.bgimage.length){
36470 cfg.cn[0].cn.push({
36472 cls: 'masonry-brick-image-view',
36477 if(this.videourl.length){
36478 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36479 // youtube support only?
36480 cfg.cn[0].cn.cn.push({
36482 cls: 'masonry-brick-image-view',
36485 allowfullscreen : true
36492 initEvents: function()
36494 switch (this.size) {
36527 this.el.on('touchstart', this.onTouchStart, this);
36528 this.el.on('touchmove', this.onTouchMove, this);
36529 this.el.on('touchend', this.onTouchEnd, this);
36530 this.el.on('contextmenu', this.onContextMenu, this);
36532 this.el.on('mouseenter' ,this.enter, this);
36533 this.el.on('mouseleave', this.leave, this);
36534 this.el.on('click', this.onClick, this);
36537 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36538 this.parent().bricks.push(this);
36543 onClick: function(e, el)
36545 var time = this.endTimer - this.startTimer;
36546 // Roo.log(e.preventDefault());
36549 e.preventDefault();
36554 if(!this.preventDefault){
36558 e.preventDefault();
36560 if (this.activeClass != '') {
36561 this.selectBrick();
36564 this.fireEvent('click', this, e);
36567 enter: function(e, el)
36569 e.preventDefault();
36571 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36575 if(this.bgimage.length && this.html.length){
36576 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36580 leave: function(e, el)
36582 e.preventDefault();
36584 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36588 if(this.bgimage.length && this.html.length){
36589 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36593 onTouchStart: function(e, el)
36595 // e.preventDefault();
36597 this.touchmoved = false;
36599 if(!this.isFitContainer){
36603 if(!this.bgimage.length || !this.html.length){
36607 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36609 this.timer = new Date().getTime();
36613 onTouchMove: function(e, el)
36615 this.touchmoved = true;
36618 onContextMenu : function(e,el)
36620 e.preventDefault();
36621 e.stopPropagation();
36625 onTouchEnd: function(e, el)
36627 // e.preventDefault();
36629 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36636 if(!this.bgimage.length || !this.html.length){
36638 if(this.href.length){
36639 window.location.href = this.href;
36645 if(!this.isFitContainer){
36649 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36651 window.location.href = this.href;
36654 //selection on single brick only
36655 selectBrick : function() {
36657 if (!this.parentId) {
36661 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36662 var index = m.selectedBrick.indexOf(this.id);
36665 m.selectedBrick.splice(index,1);
36666 this.el.removeClass(this.activeClass);
36670 for(var i = 0; i < m.selectedBrick.length; i++) {
36671 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36672 b.el.removeClass(b.activeClass);
36675 m.selectedBrick = [];
36677 m.selectedBrick.push(this.id);
36678 this.el.addClass(this.activeClass);
36682 isSelected : function(){
36683 return this.el.hasClass(this.activeClass);
36688 Roo.apply(Roo.bootstrap.MasonryBrick, {
36691 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36693 * register a Masonry Brick
36694 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36697 register : function(brick)
36699 //this.groups[brick.id] = brick;
36700 this.groups.add(brick.id, brick);
36703 * fetch a masonry brick based on the masonry brick ID
36704 * @param {string} the masonry brick to add
36705 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36708 get: function(brick_id)
36710 // if (typeof(this.groups[brick_id]) == 'undefined') {
36713 // return this.groups[brick_id] ;
36715 if(this.groups.key(brick_id)) {
36716 return this.groups.key(brick_id);
36734 * @class Roo.bootstrap.Brick
36735 * @extends Roo.bootstrap.Component
36736 * Bootstrap Brick class
36739 * Create a new Brick
36740 * @param {Object} config The config object
36743 Roo.bootstrap.Brick = function(config){
36744 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36750 * When a Brick is click
36751 * @param {Roo.bootstrap.Brick} this
36752 * @param {Roo.EventObject} e
36758 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36761 * @cfg {String} title
36765 * @cfg {String} html
36769 * @cfg {String} bgimage
36773 * @cfg {String} cls
36777 * @cfg {String} href
36781 * @cfg {String} video
36785 * @cfg {Boolean} square
36789 getAutoCreate : function()
36791 var cls = 'roo-brick';
36793 if(this.href.length){
36794 cls += ' roo-brick-link';
36797 if(this.bgimage.length){
36798 cls += ' roo-brick-image';
36801 if(!this.html.length && !this.bgimage.length){
36802 cls += ' roo-brick-center-title';
36805 if(!this.html.length && this.bgimage.length){
36806 cls += ' roo-brick-bottom-title';
36810 cls += ' ' + this.cls;
36814 tag: (this.href.length) ? 'a' : 'div',
36819 cls: 'roo-brick-paragraph',
36825 if(this.href.length){
36826 cfg.href = this.href;
36829 var cn = cfg.cn[0].cn;
36831 if(this.title.length){
36834 cls: 'roo-brick-title',
36839 if(this.html.length){
36842 cls: 'roo-brick-text',
36849 if(this.bgimage.length){
36852 cls: 'roo-brick-image-view',
36860 initEvents: function()
36862 if(this.title.length || this.html.length){
36863 this.el.on('mouseenter' ,this.enter, this);
36864 this.el.on('mouseleave', this.leave, this);
36867 Roo.EventManager.onWindowResize(this.resize, this);
36869 if(this.bgimage.length){
36870 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36871 this.imageEl.on('load', this.onImageLoad, this);
36878 onImageLoad : function()
36883 resize : function()
36885 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36887 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36889 if(this.bgimage.length){
36890 var image = this.el.select('.roo-brick-image-view', true).first();
36892 image.setWidth(paragraph.getWidth());
36895 image.setHeight(paragraph.getWidth());
36898 this.el.setHeight(image.getHeight());
36899 paragraph.setHeight(image.getHeight());
36905 enter: function(e, el)
36907 e.preventDefault();
36909 if(this.bgimage.length){
36910 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36911 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36915 leave: function(e, el)
36917 e.preventDefault();
36919 if(this.bgimage.length){
36920 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36921 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36936 * @class Roo.bootstrap.NumberField
36937 * @extends Roo.bootstrap.Input
36938 * Bootstrap NumberField class
36944 * Create a new NumberField
36945 * @param {Object} config The config object
36948 Roo.bootstrap.NumberField = function(config){
36949 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36952 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36955 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36957 allowDecimals : true,
36959 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36961 decimalSeparator : ".",
36963 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36965 decimalPrecision : 2,
36967 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36969 allowNegative : true,
36972 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36976 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36978 minValue : Number.NEGATIVE_INFINITY,
36980 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36982 maxValue : Number.MAX_VALUE,
36984 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36986 minText : "The minimum value for this field is {0}",
36988 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36990 maxText : "The maximum value for this field is {0}",
36992 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36993 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36995 nanText : "{0} is not a valid number",
36997 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36999 thousandsDelimiter : false,
37001 * @cfg {String} valueAlign alignment of value
37003 valueAlign : "left",
37005 getAutoCreate : function()
37007 var hiddenInput = {
37011 cls: 'hidden-number-input'
37015 hiddenInput.name = this.name;
37020 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37022 this.name = hiddenInput.name;
37024 if(cfg.cn.length > 0) {
37025 cfg.cn.push(hiddenInput);
37032 initEvents : function()
37034 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37036 var allowed = "0123456789";
37038 if(this.allowDecimals){
37039 allowed += this.decimalSeparator;
37042 if(this.allowNegative){
37046 if(this.thousandsDelimiter) {
37050 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37052 var keyPress = function(e){
37054 var k = e.getKey();
37056 var c = e.getCharCode();
37059 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37060 allowed.indexOf(String.fromCharCode(c)) === -1
37066 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37070 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37075 this.el.on("keypress", keyPress, this);
37078 validateValue : function(value)
37081 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37085 var num = this.parseValue(value);
37088 this.markInvalid(String.format(this.nanText, value));
37092 if(num < this.minValue){
37093 this.markInvalid(String.format(this.minText, this.minValue));
37097 if(num > this.maxValue){
37098 this.markInvalid(String.format(this.maxText, this.maxValue));
37105 getValue : function()
37107 var v = this.hiddenEl().getValue();
37109 return this.fixPrecision(this.parseValue(v));
37112 parseValue : function(value)
37114 if(this.thousandsDelimiter) {
37116 r = new RegExp(",", "g");
37117 value = value.replace(r, "");
37120 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37121 return isNaN(value) ? '' : value;
37124 fixPrecision : function(value)
37126 if(this.thousandsDelimiter) {
37128 r = new RegExp(",", "g");
37129 value = value.replace(r, "");
37132 var nan = isNaN(value);
37134 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37135 return nan ? '' : value;
37137 return parseFloat(value).toFixed(this.decimalPrecision);
37140 setValue : function(v)
37142 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37148 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37150 this.inputEl().dom.value = (v == '') ? '' :
37151 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37153 if(!this.allowZero && v === '0') {
37154 this.hiddenEl().dom.value = '';
37155 this.inputEl().dom.value = '';
37162 decimalPrecisionFcn : function(v)
37164 return Math.floor(v);
37167 beforeBlur : function()
37169 var v = this.parseValue(this.getRawValue());
37171 if(v || v === 0 || v === ''){
37176 hiddenEl : function()
37178 return this.el.select('input.hidden-number-input',true).first();
37190 * @class Roo.bootstrap.DocumentSlider
37191 * @extends Roo.bootstrap.Component
37192 * Bootstrap DocumentSlider class
37195 * Create a new DocumentViewer
37196 * @param {Object} config The config object
37199 Roo.bootstrap.DocumentSlider = function(config){
37200 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37207 * Fire after initEvent
37208 * @param {Roo.bootstrap.DocumentSlider} this
37213 * Fire after update
37214 * @param {Roo.bootstrap.DocumentSlider} this
37220 * @param {Roo.bootstrap.DocumentSlider} this
37226 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37232 getAutoCreate : function()
37236 cls : 'roo-document-slider',
37240 cls : 'roo-document-slider-header',
37244 cls : 'roo-document-slider-header-title'
37250 cls : 'roo-document-slider-body',
37254 cls : 'roo-document-slider-prev',
37258 cls : 'fa fa-chevron-left'
37264 cls : 'roo-document-slider-thumb',
37268 cls : 'roo-document-slider-image'
37274 cls : 'roo-document-slider-next',
37278 cls : 'fa fa-chevron-right'
37290 initEvents : function()
37292 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37293 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37295 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37296 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37298 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37299 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37301 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37302 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37304 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37305 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37307 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37308 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37310 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37311 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37313 this.thumbEl.on('click', this.onClick, this);
37315 this.prevIndicator.on('click', this.prev, this);
37317 this.nextIndicator.on('click', this.next, this);
37321 initial : function()
37323 if(this.files.length){
37324 this.indicator = 1;
37328 this.fireEvent('initial', this);
37331 update : function()
37333 this.imageEl.attr('src', this.files[this.indicator - 1]);
37335 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37337 this.prevIndicator.show();
37339 if(this.indicator == 1){
37340 this.prevIndicator.hide();
37343 this.nextIndicator.show();
37345 if(this.indicator == this.files.length){
37346 this.nextIndicator.hide();
37349 this.thumbEl.scrollTo('top');
37351 this.fireEvent('update', this);
37354 onClick : function(e)
37356 e.preventDefault();
37358 this.fireEvent('click', this);
37363 e.preventDefault();
37365 this.indicator = Math.max(1, this.indicator - 1);
37372 e.preventDefault();
37374 this.indicator = Math.min(this.files.length, this.indicator + 1);
37388 * @class Roo.bootstrap.RadioSet
37389 * @extends Roo.bootstrap.Input
37390 * Bootstrap RadioSet class
37391 * @cfg {String} indicatorpos (left|right) default left
37392 * @cfg {Boolean} inline (true|false) inline the element (default true)
37393 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37395 * Create a new RadioSet
37396 * @param {Object} config The config object
37399 Roo.bootstrap.RadioSet = function(config){
37401 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37405 Roo.bootstrap.RadioSet.register(this);
37410 * Fires when the element is checked or unchecked.
37411 * @param {Roo.bootstrap.RadioSet} this This radio
37412 * @param {Roo.bootstrap.Radio} item The checked item
37417 * Fires when the element is click.
37418 * @param {Roo.bootstrap.RadioSet} this This radio set
37419 * @param {Roo.bootstrap.Radio} item The checked item
37420 * @param {Roo.EventObject} e The event object
37427 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37435 indicatorpos : 'left',
37437 getAutoCreate : function()
37441 cls : 'roo-radio-set-label',
37445 html : this.fieldLabel
37449 if (Roo.bootstrap.version == 3) {
37452 if(this.indicatorpos == 'left'){
37455 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37456 tooltip : 'This field is required'
37461 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37462 tooltip : 'This field is required'
37468 cls : 'roo-radio-set-items'
37471 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37473 if (align === 'left' && this.fieldLabel.length) {
37476 cls : "roo-radio-set-right",
37482 if(this.labelWidth > 12){
37483 label.style = "width: " + this.labelWidth + 'px';
37486 if(this.labelWidth < 13 && this.labelmd == 0){
37487 this.labelmd = this.labelWidth;
37490 if(this.labellg > 0){
37491 label.cls += ' col-lg-' + this.labellg;
37492 items.cls += ' col-lg-' + (12 - this.labellg);
37495 if(this.labelmd > 0){
37496 label.cls += ' col-md-' + this.labelmd;
37497 items.cls += ' col-md-' + (12 - this.labelmd);
37500 if(this.labelsm > 0){
37501 label.cls += ' col-sm-' + this.labelsm;
37502 items.cls += ' col-sm-' + (12 - this.labelsm);
37505 if(this.labelxs > 0){
37506 label.cls += ' col-xs-' + this.labelxs;
37507 items.cls += ' col-xs-' + (12 - this.labelxs);
37513 cls : 'roo-radio-set',
37517 cls : 'roo-radio-set-input',
37520 value : this.value ? this.value : ''
37527 if(this.weight.length){
37528 cfg.cls += ' roo-radio-' + this.weight;
37532 cfg.cls += ' roo-radio-set-inline';
37536 ['xs','sm','md','lg'].map(function(size){
37537 if (settings[size]) {
37538 cfg.cls += ' col-' + size + '-' + settings[size];
37546 initEvents : function()
37548 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37549 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37551 if(!this.fieldLabel.length){
37552 this.labelEl.hide();
37555 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37556 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37558 this.indicator = this.indicatorEl();
37560 if(this.indicator){
37561 this.indicator.addClass('invisible');
37564 this.originalValue = this.getValue();
37568 inputEl: function ()
37570 return this.el.select('.roo-radio-set-input', true).first();
37573 getChildContainer : function()
37575 return this.itemsEl;
37578 register : function(item)
37580 this.radioes.push(item);
37584 validate : function()
37586 if(this.getVisibilityEl().hasClass('hidden')){
37592 Roo.each(this.radioes, function(i){
37601 if(this.allowBlank) {
37605 if(this.disabled || valid){
37610 this.markInvalid();
37615 markValid : function()
37617 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37618 this.indicatorEl().removeClass('visible');
37619 this.indicatorEl().addClass('invisible');
37623 if (Roo.bootstrap.version == 3) {
37624 this.el.removeClass([this.invalidClass, this.validClass]);
37625 this.el.addClass(this.validClass);
37627 this.el.removeClass(['is-invalid','is-valid']);
37628 this.el.addClass(['is-valid']);
37630 this.fireEvent('valid', this);
37633 markInvalid : function(msg)
37635 if(this.allowBlank || this.disabled){
37639 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37640 this.indicatorEl().removeClass('invisible');
37641 this.indicatorEl().addClass('visible');
37643 if (Roo.bootstrap.version == 3) {
37644 this.el.removeClass([this.invalidClass, this.validClass]);
37645 this.el.addClass(this.invalidClass);
37647 this.el.removeClass(['is-invalid','is-valid']);
37648 this.el.addClass(['is-invalid']);
37651 this.fireEvent('invalid', this, msg);
37655 setValue : function(v, suppressEvent)
37657 if(this.value === v){
37664 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37667 Roo.each(this.radioes, function(i){
37669 i.el.removeClass('checked');
37672 Roo.each(this.radioes, function(i){
37674 if(i.value === v || i.value.toString() === v.toString()){
37676 i.el.addClass('checked');
37678 if(suppressEvent !== true){
37679 this.fireEvent('check', this, i);
37690 clearInvalid : function(){
37692 if(!this.el || this.preventMark){
37696 this.el.removeClass([this.invalidClass]);
37698 this.fireEvent('valid', this);
37703 Roo.apply(Roo.bootstrap.RadioSet, {
37707 register : function(set)
37709 this.groups[set.name] = set;
37712 get: function(name)
37714 if (typeof(this.groups[name]) == 'undefined') {
37718 return this.groups[name] ;
37724 * Ext JS Library 1.1.1
37725 * Copyright(c) 2006-2007, Ext JS, LLC.
37727 * Originally Released Under LGPL - original licence link has changed is not relivant.
37730 * <script type="text/javascript">
37735 * @class Roo.bootstrap.SplitBar
37736 * @extends Roo.util.Observable
37737 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37741 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37742 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37743 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37744 split.minSize = 100;
37745 split.maxSize = 600;
37746 split.animate = true;
37747 split.on('moved', splitterMoved);
37750 * Create a new SplitBar
37751 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37752 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37753 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37754 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37755 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37756 position of the SplitBar).
37758 Roo.bootstrap.SplitBar = function(cfg){
37763 // dragElement : elm
37764 // resizingElement: el,
37766 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37767 // placement : Roo.bootstrap.SplitBar.LEFT ,
37768 // existingProxy ???
37771 this.el = Roo.get(cfg.dragElement, true);
37772 this.el.dom.unselectable = "on";
37774 this.resizingEl = Roo.get(cfg.resizingElement, true);
37778 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37779 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37782 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37785 * The minimum size of the resizing element. (Defaults to 0)
37791 * The maximum size of the resizing element. (Defaults to 2000)
37794 this.maxSize = 2000;
37797 * Whether to animate the transition to the new size
37800 this.animate = false;
37803 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37806 this.useShim = false;
37811 if(!cfg.existingProxy){
37813 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37815 this.proxy = Roo.get(cfg.existingProxy).dom;
37818 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37821 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37824 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37827 this.dragSpecs = {};
37830 * @private The adapter to use to positon and resize elements
37832 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37833 this.adapter.init(this);
37835 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37837 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37838 this.el.addClass("roo-splitbar-h");
37841 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37842 this.el.addClass("roo-splitbar-v");
37848 * Fires when the splitter is moved (alias for {@link #event-moved})
37849 * @param {Roo.bootstrap.SplitBar} this
37850 * @param {Number} newSize the new width or height
37855 * Fires when the splitter is moved
37856 * @param {Roo.bootstrap.SplitBar} this
37857 * @param {Number} newSize the new width or height
37861 * @event beforeresize
37862 * Fires before the splitter is dragged
37863 * @param {Roo.bootstrap.SplitBar} this
37865 "beforeresize" : true,
37867 "beforeapply" : true
37870 Roo.util.Observable.call(this);
37873 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37874 onStartProxyDrag : function(x, y){
37875 this.fireEvent("beforeresize", this);
37877 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37879 o.enableDisplayMode("block");
37880 // all splitbars share the same overlay
37881 Roo.bootstrap.SplitBar.prototype.overlay = o;
37883 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37884 this.overlay.show();
37885 Roo.get(this.proxy).setDisplayed("block");
37886 var size = this.adapter.getElementSize(this);
37887 this.activeMinSize = this.getMinimumSize();;
37888 this.activeMaxSize = this.getMaximumSize();;
37889 var c1 = size - this.activeMinSize;
37890 var c2 = Math.max(this.activeMaxSize - size, 0);
37891 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37892 this.dd.resetConstraints();
37893 this.dd.setXConstraint(
37894 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37895 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37897 this.dd.setYConstraint(0, 0);
37899 this.dd.resetConstraints();
37900 this.dd.setXConstraint(0, 0);
37901 this.dd.setYConstraint(
37902 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37903 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37906 this.dragSpecs.startSize = size;
37907 this.dragSpecs.startPoint = [x, y];
37908 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37912 * @private Called after the drag operation by the DDProxy
37914 onEndProxyDrag : function(e){
37915 Roo.get(this.proxy).setDisplayed(false);
37916 var endPoint = Roo.lib.Event.getXY(e);
37918 this.overlay.hide();
37921 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37922 newSize = this.dragSpecs.startSize +
37923 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37924 endPoint[0] - this.dragSpecs.startPoint[0] :
37925 this.dragSpecs.startPoint[0] - endPoint[0]
37928 newSize = this.dragSpecs.startSize +
37929 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37930 endPoint[1] - this.dragSpecs.startPoint[1] :
37931 this.dragSpecs.startPoint[1] - endPoint[1]
37934 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37935 if(newSize != this.dragSpecs.startSize){
37936 if(this.fireEvent('beforeapply', this, newSize) !== false){
37937 this.adapter.setElementSize(this, newSize);
37938 this.fireEvent("moved", this, newSize);
37939 this.fireEvent("resize", this, newSize);
37945 * Get the adapter this SplitBar uses
37946 * @return The adapter object
37948 getAdapter : function(){
37949 return this.adapter;
37953 * Set the adapter this SplitBar uses
37954 * @param {Object} adapter A SplitBar adapter object
37956 setAdapter : function(adapter){
37957 this.adapter = adapter;
37958 this.adapter.init(this);
37962 * Gets the minimum size for the resizing element
37963 * @return {Number} The minimum size
37965 getMinimumSize : function(){
37966 return this.minSize;
37970 * Sets the minimum size for the resizing element
37971 * @param {Number} minSize The minimum size
37973 setMinimumSize : function(minSize){
37974 this.minSize = minSize;
37978 * Gets the maximum size for the resizing element
37979 * @return {Number} The maximum size
37981 getMaximumSize : function(){
37982 return this.maxSize;
37986 * Sets the maximum size for the resizing element
37987 * @param {Number} maxSize The maximum size
37989 setMaximumSize : function(maxSize){
37990 this.maxSize = maxSize;
37994 * Sets the initialize size for the resizing element
37995 * @param {Number} size The initial size
37997 setCurrentSize : function(size){
37998 var oldAnimate = this.animate;
37999 this.animate = false;
38000 this.adapter.setElementSize(this, size);
38001 this.animate = oldAnimate;
38005 * Destroy this splitbar.
38006 * @param {Boolean} removeEl True to remove the element
38008 destroy : function(removeEl){
38010 this.shim.remove();
38013 this.proxy.parentNode.removeChild(this.proxy);
38021 * @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.
38023 Roo.bootstrap.SplitBar.createProxy = function(dir){
38024 var proxy = new Roo.Element(document.createElement("div"));
38025 proxy.unselectable();
38026 var cls = 'roo-splitbar-proxy';
38027 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38028 document.body.appendChild(proxy.dom);
38033 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38034 * Default Adapter. It assumes the splitter and resizing element are not positioned
38035 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38037 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38040 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38041 // do nothing for now
38042 init : function(s){
38046 * Called before drag operations to get the current size of the resizing element.
38047 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38049 getElementSize : function(s){
38050 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38051 return s.resizingEl.getWidth();
38053 return s.resizingEl.getHeight();
38058 * Called after drag operations to set the size of the resizing element.
38059 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38060 * @param {Number} newSize The new size to set
38061 * @param {Function} onComplete A function to be invoked when resizing is complete
38063 setElementSize : function(s, newSize, onComplete){
38064 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38066 s.resizingEl.setWidth(newSize);
38068 onComplete(s, newSize);
38071 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38076 s.resizingEl.setHeight(newSize);
38078 onComplete(s, newSize);
38081 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38088 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38089 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38090 * Adapter that moves the splitter element to align with the resized sizing element.
38091 * Used with an absolute positioned SplitBar.
38092 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38093 * document.body, make sure you assign an id to the body element.
38095 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38096 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38097 this.container = Roo.get(container);
38100 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38101 init : function(s){
38102 this.basic.init(s);
38105 getElementSize : function(s){
38106 return this.basic.getElementSize(s);
38109 setElementSize : function(s, newSize, onComplete){
38110 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38113 moveSplitter : function(s){
38114 var yes = Roo.bootstrap.SplitBar;
38115 switch(s.placement){
38117 s.el.setX(s.resizingEl.getRight());
38120 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38123 s.el.setY(s.resizingEl.getBottom());
38126 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38133 * Orientation constant - Create a vertical SplitBar
38137 Roo.bootstrap.SplitBar.VERTICAL = 1;
38140 * Orientation constant - Create a horizontal SplitBar
38144 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38147 * Placement constant - The resizing element is to the left of the splitter element
38151 Roo.bootstrap.SplitBar.LEFT = 1;
38154 * Placement constant - The resizing element is to the right of the splitter element
38158 Roo.bootstrap.SplitBar.RIGHT = 2;
38161 * Placement constant - The resizing element is positioned above the splitter element
38165 Roo.bootstrap.SplitBar.TOP = 3;
38168 * Placement constant - The resizing element is positioned under splitter element
38172 Roo.bootstrap.SplitBar.BOTTOM = 4;
38173 Roo.namespace("Roo.bootstrap.layout");/*
38175 * Ext JS Library 1.1.1
38176 * Copyright(c) 2006-2007, Ext JS, LLC.
38178 * Originally Released Under LGPL - original licence link has changed is not relivant.
38181 * <script type="text/javascript">
38185 * @class Roo.bootstrap.layout.Manager
38186 * @extends Roo.bootstrap.Component
38187 * Base class for layout managers.
38189 Roo.bootstrap.layout.Manager = function(config)
38191 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38197 /** false to disable window resize monitoring @type Boolean */
38198 this.monitorWindowResize = true;
38203 * Fires when a layout is performed.
38204 * @param {Roo.LayoutManager} this
38208 * @event regionresized
38209 * Fires when the user resizes a region.
38210 * @param {Roo.LayoutRegion} region The resized region
38211 * @param {Number} newSize The new size (width for east/west, height for north/south)
38213 "regionresized" : true,
38215 * @event regioncollapsed
38216 * Fires when a region is collapsed.
38217 * @param {Roo.LayoutRegion} region The collapsed region
38219 "regioncollapsed" : true,
38221 * @event regionexpanded
38222 * Fires when a region is expanded.
38223 * @param {Roo.LayoutRegion} region The expanded region
38225 "regionexpanded" : true
38227 this.updating = false;
38230 this.el = Roo.get(config.el);
38236 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38241 monitorWindowResize : true,
38247 onRender : function(ct, position)
38250 this.el = Roo.get(ct);
38253 //this.fireEvent('render',this);
38257 initEvents: function()
38261 // ie scrollbar fix
38262 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38263 document.body.scroll = "no";
38264 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38265 this.el.position('relative');
38267 this.id = this.el.id;
38268 this.el.addClass("roo-layout-container");
38269 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38270 if(this.el.dom != document.body ) {
38271 this.el.on('resize', this.layout,this);
38272 this.el.on('show', this.layout,this);
38278 * Returns true if this layout is currently being updated
38279 * @return {Boolean}
38281 isUpdating : function(){
38282 return this.updating;
38286 * Suspend the LayoutManager from doing auto-layouts while
38287 * making multiple add or remove calls
38289 beginUpdate : function(){
38290 this.updating = true;
38294 * Restore auto-layouts and optionally disable the manager from performing a layout
38295 * @param {Boolean} noLayout true to disable a layout update
38297 endUpdate : function(noLayout){
38298 this.updating = false;
38304 layout: function(){
38308 onRegionResized : function(region, newSize){
38309 this.fireEvent("regionresized", region, newSize);
38313 onRegionCollapsed : function(region){
38314 this.fireEvent("regioncollapsed", region);
38317 onRegionExpanded : function(region){
38318 this.fireEvent("regionexpanded", region);
38322 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38323 * performs box-model adjustments.
38324 * @return {Object} The size as an object {width: (the width), height: (the height)}
38326 getViewSize : function()
38329 if(this.el.dom != document.body){
38330 size = this.el.getSize();
38332 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38334 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38335 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38340 * Returns the Element this layout is bound to.
38341 * @return {Roo.Element}
38343 getEl : function(){
38348 * Returns the specified region.
38349 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38350 * @return {Roo.LayoutRegion}
38352 getRegion : function(target){
38353 return this.regions[target.toLowerCase()];
38356 onWindowResize : function(){
38357 if(this.monitorWindowResize){
38364 * Ext JS Library 1.1.1
38365 * Copyright(c) 2006-2007, Ext JS, LLC.
38367 * Originally Released Under LGPL - original licence link has changed is not relivant.
38370 * <script type="text/javascript">
38373 * @class Roo.bootstrap.layout.Border
38374 * @extends Roo.bootstrap.layout.Manager
38376 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38377 * please see: examples/bootstrap/nested.html<br><br>
38379 <b>The container the layout is rendered into can be either the body element or any other element.
38380 If it is not the body element, the container needs to either be an absolute positioned element,
38381 or you will need to add "position:relative" to the css of the container. You will also need to specify
38382 the container size if it is not the body element.</b>
38385 * Create a new Border
38386 * @param {Object} config Configuration options
38388 Roo.bootstrap.layout.Border = function(config){
38389 config = config || {};
38390 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38394 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38395 if(config[region]){
38396 config[region].region = region;
38397 this.addRegion(config[region]);
38403 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38405 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38407 parent : false, // this might point to a 'nest' or a ???
38410 * Creates and adds a new region if it doesn't already exist.
38411 * @param {String} target The target region key (north, south, east, west or center).
38412 * @param {Object} config The regions config object
38413 * @return {BorderLayoutRegion} The new region
38415 addRegion : function(config)
38417 if(!this.regions[config.region]){
38418 var r = this.factory(config);
38419 this.bindRegion(r);
38421 return this.regions[config.region];
38425 bindRegion : function(r){
38426 this.regions[r.config.region] = r;
38428 r.on("visibilitychange", this.layout, this);
38429 r.on("paneladded", this.layout, this);
38430 r.on("panelremoved", this.layout, this);
38431 r.on("invalidated", this.layout, this);
38432 r.on("resized", this.onRegionResized, this);
38433 r.on("collapsed", this.onRegionCollapsed, this);
38434 r.on("expanded", this.onRegionExpanded, this);
38438 * Performs a layout update.
38440 layout : function()
38442 if(this.updating) {
38446 // render all the rebions if they have not been done alreayd?
38447 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38448 if(this.regions[region] && !this.regions[region].bodyEl){
38449 this.regions[region].onRender(this.el)
38453 var size = this.getViewSize();
38454 var w = size.width;
38455 var h = size.height;
38460 //var x = 0, y = 0;
38462 var rs = this.regions;
38463 var north = rs["north"];
38464 var south = rs["south"];
38465 var west = rs["west"];
38466 var east = rs["east"];
38467 var center = rs["center"];
38468 //if(this.hideOnLayout){ // not supported anymore
38469 //c.el.setStyle("display", "none");
38471 if(north && north.isVisible()){
38472 var b = north.getBox();
38473 var m = north.getMargins();
38474 b.width = w - (m.left+m.right);
38477 centerY = b.height + b.y + m.bottom;
38478 centerH -= centerY;
38479 north.updateBox(this.safeBox(b));
38481 if(south && south.isVisible()){
38482 var b = south.getBox();
38483 var m = south.getMargins();
38484 b.width = w - (m.left+m.right);
38486 var totalHeight = (b.height + m.top + m.bottom);
38487 b.y = h - totalHeight + m.top;
38488 centerH -= totalHeight;
38489 south.updateBox(this.safeBox(b));
38491 if(west && west.isVisible()){
38492 var b = west.getBox();
38493 var m = west.getMargins();
38494 b.height = centerH - (m.top+m.bottom);
38496 b.y = centerY + m.top;
38497 var totalWidth = (b.width + m.left + m.right);
38498 centerX += totalWidth;
38499 centerW -= totalWidth;
38500 west.updateBox(this.safeBox(b));
38502 if(east && east.isVisible()){
38503 var b = east.getBox();
38504 var m = east.getMargins();
38505 b.height = centerH - (m.top+m.bottom);
38506 var totalWidth = (b.width + m.left + m.right);
38507 b.x = w - totalWidth + m.left;
38508 b.y = centerY + m.top;
38509 centerW -= totalWidth;
38510 east.updateBox(this.safeBox(b));
38513 var m = center.getMargins();
38515 x: centerX + m.left,
38516 y: centerY + m.top,
38517 width: centerW - (m.left+m.right),
38518 height: centerH - (m.top+m.bottom)
38520 //if(this.hideOnLayout){
38521 //center.el.setStyle("display", "block");
38523 center.updateBox(this.safeBox(centerBox));
38526 this.fireEvent("layout", this);
38530 safeBox : function(box){
38531 box.width = Math.max(0, box.width);
38532 box.height = Math.max(0, box.height);
38537 * Adds a ContentPanel (or subclass) to this layout.
38538 * @param {String} target The target region key (north, south, east, west or center).
38539 * @param {Roo.ContentPanel} panel The panel to add
38540 * @return {Roo.ContentPanel} The added panel
38542 add : function(target, panel){
38544 target = target.toLowerCase();
38545 return this.regions[target].add(panel);
38549 * Remove a ContentPanel (or subclass) to this layout.
38550 * @param {String} target The target region key (north, south, east, west or center).
38551 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38552 * @return {Roo.ContentPanel} The removed panel
38554 remove : function(target, panel){
38555 target = target.toLowerCase();
38556 return this.regions[target].remove(panel);
38560 * Searches all regions for a panel with the specified id
38561 * @param {String} panelId
38562 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38564 findPanel : function(panelId){
38565 var rs = this.regions;
38566 for(var target in rs){
38567 if(typeof rs[target] != "function"){
38568 var p = rs[target].getPanel(panelId);
38578 * Searches all regions for a panel with the specified id and activates (shows) it.
38579 * @param {String/ContentPanel} panelId The panels id or the panel itself
38580 * @return {Roo.ContentPanel} The shown panel or null
38582 showPanel : function(panelId) {
38583 var rs = this.regions;
38584 for(var target in rs){
38585 var r = rs[target];
38586 if(typeof r != "function"){
38587 if(r.hasPanel(panelId)){
38588 return r.showPanel(panelId);
38596 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38597 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38600 restoreState : function(provider){
38602 provider = Roo.state.Manager;
38604 var sm = new Roo.LayoutStateManager();
38605 sm.init(this, provider);
38611 * Adds a xtype elements to the layout.
38615 xtype : 'ContentPanel',
38622 xtype : 'NestedLayoutPanel',
38628 items : [ ... list of content panels or nested layout panels.. ]
38632 * @param {Object} cfg Xtype definition of item to add.
38634 addxtype : function(cfg)
38636 // basically accepts a pannel...
38637 // can accept a layout region..!?!?
38638 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38641 // theory? children can only be panels??
38643 //if (!cfg.xtype.match(/Panel$/)) {
38648 if (typeof(cfg.region) == 'undefined') {
38649 Roo.log("Failed to add Panel, region was not set");
38653 var region = cfg.region;
38659 xitems = cfg.items;
38664 if ( region == 'center') {
38665 Roo.log("Center: " + cfg.title);
38671 case 'Content': // ContentPanel (el, cfg)
38672 case 'Scroll': // ContentPanel (el, cfg)
38674 cfg.autoCreate = cfg.autoCreate || true;
38675 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38677 // var el = this.el.createChild();
38678 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38681 this.add(region, ret);
38685 case 'TreePanel': // our new panel!
38686 cfg.el = this.el.createChild();
38687 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38688 this.add(region, ret);
38693 // create a new Layout (which is a Border Layout...
38695 var clayout = cfg.layout;
38696 clayout.el = this.el.createChild();
38697 clayout.items = clayout.items || [];
38701 // replace this exitems with the clayout ones..
38702 xitems = clayout.items;
38704 // force background off if it's in center...
38705 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38706 cfg.background = false;
38708 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38711 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38712 //console.log('adding nested layout panel ' + cfg.toSource());
38713 this.add(region, ret);
38714 nb = {}; /// find first...
38719 // needs grid and region
38721 //var el = this.getRegion(region).el.createChild();
38723 *var el = this.el.createChild();
38724 // create the grid first...
38725 cfg.grid.container = el;
38726 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38729 if (region == 'center' && this.active ) {
38730 cfg.background = false;
38733 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38735 this.add(region, ret);
38737 if (cfg.background) {
38738 // render grid on panel activation (if panel background)
38739 ret.on('activate', function(gp) {
38740 if (!gp.grid.rendered) {
38741 // gp.grid.render(el);
38745 // cfg.grid.render(el);
38751 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38752 // it was the old xcomponent building that caused this before.
38753 // espeically if border is the top element in the tree.
38763 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38765 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38766 this.add(region, ret);
38770 throw "Can not add '" + cfg.xtype + "' to Border";
38776 this.beginUpdate();
38780 Roo.each(xitems, function(i) {
38781 region = nb && i.region ? i.region : false;
38783 var add = ret.addxtype(i);
38786 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38787 if (!i.background) {
38788 abn[region] = nb[region] ;
38795 // make the last non-background panel active..
38796 //if (nb) { Roo.log(abn); }
38799 for(var r in abn) {
38800 region = this.getRegion(r);
38802 // tried using nb[r], but it does not work..
38804 region.showPanel(abn[r]);
38815 factory : function(cfg)
38818 var validRegions = Roo.bootstrap.layout.Border.regions;
38820 var target = cfg.region;
38823 var r = Roo.bootstrap.layout;
38827 return new r.North(cfg);
38829 return new r.South(cfg);
38831 return new r.East(cfg);
38833 return new r.West(cfg);
38835 return new r.Center(cfg);
38837 throw 'Layout region "'+target+'" not supported.';
38844 * Ext JS Library 1.1.1
38845 * Copyright(c) 2006-2007, Ext JS, LLC.
38847 * Originally Released Under LGPL - original licence link has changed is not relivant.
38850 * <script type="text/javascript">
38854 * @class Roo.bootstrap.layout.Basic
38855 * @extends Roo.util.Observable
38856 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38857 * and does not have a titlebar, tabs or any other features. All it does is size and position
38858 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38859 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38860 * @cfg {string} region the region that it inhabits..
38861 * @cfg {bool} skipConfig skip config?
38865 Roo.bootstrap.layout.Basic = function(config){
38867 this.mgr = config.mgr;
38869 this.position = config.region;
38871 var skipConfig = config.skipConfig;
38875 * @scope Roo.BasicLayoutRegion
38879 * @event beforeremove
38880 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38881 * @param {Roo.LayoutRegion} this
38882 * @param {Roo.ContentPanel} panel The panel
38883 * @param {Object} e The cancel event object
38885 "beforeremove" : true,
38887 * @event invalidated
38888 * Fires when the layout for this region is changed.
38889 * @param {Roo.LayoutRegion} this
38891 "invalidated" : true,
38893 * @event visibilitychange
38894 * Fires when this region is shown or hidden
38895 * @param {Roo.LayoutRegion} this
38896 * @param {Boolean} visibility true or false
38898 "visibilitychange" : true,
38900 * @event paneladded
38901 * Fires when a panel is added.
38902 * @param {Roo.LayoutRegion} this
38903 * @param {Roo.ContentPanel} panel The panel
38905 "paneladded" : true,
38907 * @event panelremoved
38908 * Fires when a panel is removed.
38909 * @param {Roo.LayoutRegion} this
38910 * @param {Roo.ContentPanel} panel The panel
38912 "panelremoved" : true,
38914 * @event beforecollapse
38915 * Fires when this region before collapse.
38916 * @param {Roo.LayoutRegion} this
38918 "beforecollapse" : true,
38921 * Fires when this region is collapsed.
38922 * @param {Roo.LayoutRegion} this
38924 "collapsed" : true,
38927 * Fires when this region is expanded.
38928 * @param {Roo.LayoutRegion} this
38933 * Fires when this region is slid into view.
38934 * @param {Roo.LayoutRegion} this
38936 "slideshow" : true,
38939 * Fires when this region slides out of view.
38940 * @param {Roo.LayoutRegion} this
38942 "slidehide" : true,
38944 * @event panelactivated
38945 * Fires when a panel is activated.
38946 * @param {Roo.LayoutRegion} this
38947 * @param {Roo.ContentPanel} panel The activated panel
38949 "panelactivated" : true,
38952 * Fires when the user resizes this region.
38953 * @param {Roo.LayoutRegion} this
38954 * @param {Number} newSize The new size (width for east/west, height for north/south)
38958 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38959 this.panels = new Roo.util.MixedCollection();
38960 this.panels.getKey = this.getPanelId.createDelegate(this);
38962 this.activePanel = null;
38963 // ensure listeners are added...
38965 if (config.listeners || config.events) {
38966 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38967 listeners : config.listeners || {},
38968 events : config.events || {}
38972 if(skipConfig !== true){
38973 this.applyConfig(config);
38977 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38979 getPanelId : function(p){
38983 applyConfig : function(config){
38984 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38985 this.config = config;
38990 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38991 * the width, for horizontal (north, south) the height.
38992 * @param {Number} newSize The new width or height
38994 resizeTo : function(newSize){
38995 var el = this.el ? this.el :
38996 (this.activePanel ? this.activePanel.getEl() : null);
38998 switch(this.position){
39001 el.setWidth(newSize);
39002 this.fireEvent("resized", this, newSize);
39006 el.setHeight(newSize);
39007 this.fireEvent("resized", this, newSize);
39013 getBox : function(){
39014 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39017 getMargins : function(){
39018 return this.margins;
39021 updateBox : function(box){
39023 var el = this.activePanel.getEl();
39024 el.dom.style.left = box.x + "px";
39025 el.dom.style.top = box.y + "px";
39026 this.activePanel.setSize(box.width, box.height);
39030 * Returns the container element for this region.
39031 * @return {Roo.Element}
39033 getEl : function(){
39034 return this.activePanel;
39038 * Returns true if this region is currently visible.
39039 * @return {Boolean}
39041 isVisible : function(){
39042 return this.activePanel ? true : false;
39045 setActivePanel : function(panel){
39046 panel = this.getPanel(panel);
39047 if(this.activePanel && this.activePanel != panel){
39048 this.activePanel.setActiveState(false);
39049 this.activePanel.getEl().setLeftTop(-10000,-10000);
39051 this.activePanel = panel;
39052 panel.setActiveState(true);
39054 panel.setSize(this.box.width, this.box.height);
39056 this.fireEvent("panelactivated", this, panel);
39057 this.fireEvent("invalidated");
39061 * Show the specified panel.
39062 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39063 * @return {Roo.ContentPanel} The shown panel or null
39065 showPanel : function(panel){
39066 panel = this.getPanel(panel);
39068 this.setActivePanel(panel);
39074 * Get the active panel for this region.
39075 * @return {Roo.ContentPanel} The active panel or null
39077 getActivePanel : function(){
39078 return this.activePanel;
39082 * Add the passed ContentPanel(s)
39083 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39084 * @return {Roo.ContentPanel} The panel added (if only one was added)
39086 add : function(panel){
39087 if(arguments.length > 1){
39088 for(var i = 0, len = arguments.length; i < len; i++) {
39089 this.add(arguments[i]);
39093 if(this.hasPanel(panel)){
39094 this.showPanel(panel);
39097 var el = panel.getEl();
39098 if(el.dom.parentNode != this.mgr.el.dom){
39099 this.mgr.el.dom.appendChild(el.dom);
39101 if(panel.setRegion){
39102 panel.setRegion(this);
39104 this.panels.add(panel);
39105 el.setStyle("position", "absolute");
39106 if(!panel.background){
39107 this.setActivePanel(panel);
39108 if(this.config.initialSize && this.panels.getCount()==1){
39109 this.resizeTo(this.config.initialSize);
39112 this.fireEvent("paneladded", this, panel);
39117 * Returns true if the panel is in this region.
39118 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39119 * @return {Boolean}
39121 hasPanel : function(panel){
39122 if(typeof panel == "object"){ // must be panel obj
39123 panel = panel.getId();
39125 return this.getPanel(panel) ? true : false;
39129 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39130 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39131 * @param {Boolean} preservePanel Overrides the config preservePanel option
39132 * @return {Roo.ContentPanel} The panel that was removed
39134 remove : function(panel, preservePanel){
39135 panel = this.getPanel(panel);
39140 this.fireEvent("beforeremove", this, panel, e);
39141 if(e.cancel === true){
39144 var panelId = panel.getId();
39145 this.panels.removeKey(panelId);
39150 * Returns the panel specified or null if it's not in this region.
39151 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39152 * @return {Roo.ContentPanel}
39154 getPanel : function(id){
39155 if(typeof id == "object"){ // must be panel obj
39158 return this.panels.get(id);
39162 * Returns this regions position (north/south/east/west/center).
39165 getPosition: function(){
39166 return this.position;
39170 * Ext JS Library 1.1.1
39171 * Copyright(c) 2006-2007, Ext JS, LLC.
39173 * Originally Released Under LGPL - original licence link has changed is not relivant.
39176 * <script type="text/javascript">
39180 * @class Roo.bootstrap.layout.Region
39181 * @extends Roo.bootstrap.layout.Basic
39182 * This class represents a region in a layout manager.
39184 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39185 * @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})
39186 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39187 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39188 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39189 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39190 * @cfg {String} title The title for the region (overrides panel titles)
39191 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39192 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39193 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39194 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39195 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39196 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39197 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39198 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39199 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39200 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39202 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39203 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39204 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39205 * @cfg {Number} width For East/West panels
39206 * @cfg {Number} height For North/South panels
39207 * @cfg {Boolean} split To show the splitter
39208 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39210 * @cfg {string} cls Extra CSS classes to add to region
39212 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39213 * @cfg {string} region the region that it inhabits..
39216 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39217 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39219 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39220 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39221 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39223 Roo.bootstrap.layout.Region = function(config)
39225 this.applyConfig(config);
39227 var mgr = config.mgr;
39228 var pos = config.region;
39229 config.skipConfig = true;
39230 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39233 this.onRender(mgr.el);
39236 this.visible = true;
39237 this.collapsed = false;
39238 this.unrendered_panels = [];
39241 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39243 position: '', // set by wrapper (eg. north/south etc..)
39244 unrendered_panels : null, // unrendered panels.
39246 tabPosition : false,
39248 mgr: false, // points to 'Border'
39251 createBody : function(){
39252 /** This region's body element
39253 * @type Roo.Element */
39254 this.bodyEl = this.el.createChild({
39256 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39260 onRender: function(ctr, pos)
39262 var dh = Roo.DomHelper;
39263 /** This region's container element
39264 * @type Roo.Element */
39265 this.el = dh.append(ctr.dom, {
39267 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39269 /** This region's title element
39270 * @type Roo.Element */
39272 this.titleEl = dh.append(this.el.dom, {
39274 unselectable: "on",
39275 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39277 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39278 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39282 this.titleEl.enableDisplayMode();
39283 /** This region's title text element
39284 * @type HTMLElement */
39285 this.titleTextEl = this.titleEl.dom.firstChild;
39286 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39288 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39289 this.closeBtn.enableDisplayMode();
39290 this.closeBtn.on("click", this.closeClicked, this);
39291 this.closeBtn.hide();
39293 this.createBody(this.config);
39294 if(this.config.hideWhenEmpty){
39296 this.on("paneladded", this.validateVisibility, this);
39297 this.on("panelremoved", this.validateVisibility, this);
39299 if(this.autoScroll){
39300 this.bodyEl.setStyle("overflow", "auto");
39302 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39304 //if(c.titlebar !== false){
39305 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39306 this.titleEl.hide();
39308 this.titleEl.show();
39309 if(this.config.title){
39310 this.titleTextEl.innerHTML = this.config.title;
39314 if(this.config.collapsed){
39315 this.collapse(true);
39317 if(this.config.hidden){
39321 if (this.unrendered_panels && this.unrendered_panels.length) {
39322 for (var i =0;i< this.unrendered_panels.length; i++) {
39323 this.add(this.unrendered_panels[i]);
39325 this.unrendered_panels = null;
39331 applyConfig : function(c)
39334 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39335 var dh = Roo.DomHelper;
39336 if(c.titlebar !== false){
39337 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39338 this.collapseBtn.on("click", this.collapse, this);
39339 this.collapseBtn.enableDisplayMode();
39341 if(c.showPin === true || this.showPin){
39342 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39343 this.stickBtn.enableDisplayMode();
39344 this.stickBtn.on("click", this.expand, this);
39345 this.stickBtn.hide();
39350 /** This region's collapsed element
39351 * @type Roo.Element */
39354 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39355 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39358 if(c.floatable !== false){
39359 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39360 this.collapsedEl.on("click", this.collapseClick, this);
39363 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39364 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39365 id: "message", unselectable: "on", style:{"float":"left"}});
39366 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39368 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39369 this.expandBtn.on("click", this.expand, this);
39373 if(this.collapseBtn){
39374 this.collapseBtn.setVisible(c.collapsible == true);
39377 this.cmargins = c.cmargins || this.cmargins ||
39378 (this.position == "west" || this.position == "east" ?
39379 {top: 0, left: 2, right:2, bottom: 0} :
39380 {top: 2, left: 0, right:0, bottom: 2});
39382 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39385 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39387 this.autoScroll = c.autoScroll || false;
39392 this.duration = c.duration || .30;
39393 this.slideDuration = c.slideDuration || .45;
39398 * Returns true if this region is currently visible.
39399 * @return {Boolean}
39401 isVisible : function(){
39402 return this.visible;
39406 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39407 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39409 //setCollapsedTitle : function(title){
39410 // title = title || " ";
39411 // if(this.collapsedTitleTextEl){
39412 // this.collapsedTitleTextEl.innerHTML = title;
39416 getBox : function(){
39418 // if(!this.collapsed){
39419 b = this.el.getBox(false, true);
39421 // b = this.collapsedEl.getBox(false, true);
39426 getMargins : function(){
39427 return this.margins;
39428 //return this.collapsed ? this.cmargins : this.margins;
39431 highlight : function(){
39432 this.el.addClass("x-layout-panel-dragover");
39435 unhighlight : function(){
39436 this.el.removeClass("x-layout-panel-dragover");
39439 updateBox : function(box)
39441 if (!this.bodyEl) {
39442 return; // not rendered yet..
39446 if(!this.collapsed){
39447 this.el.dom.style.left = box.x + "px";
39448 this.el.dom.style.top = box.y + "px";
39449 this.updateBody(box.width, box.height);
39451 this.collapsedEl.dom.style.left = box.x + "px";
39452 this.collapsedEl.dom.style.top = box.y + "px";
39453 this.collapsedEl.setSize(box.width, box.height);
39456 this.tabs.autoSizeTabs();
39460 updateBody : function(w, h)
39463 this.el.setWidth(w);
39464 w -= this.el.getBorderWidth("rl");
39465 if(this.config.adjustments){
39466 w += this.config.adjustments[0];
39469 if(h !== null && h > 0){
39470 this.el.setHeight(h);
39471 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39472 h -= this.el.getBorderWidth("tb");
39473 if(this.config.adjustments){
39474 h += this.config.adjustments[1];
39476 this.bodyEl.setHeight(h);
39478 h = this.tabs.syncHeight(h);
39481 if(this.panelSize){
39482 w = w !== null ? w : this.panelSize.width;
39483 h = h !== null ? h : this.panelSize.height;
39485 if(this.activePanel){
39486 var el = this.activePanel.getEl();
39487 w = w !== null ? w : el.getWidth();
39488 h = h !== null ? h : el.getHeight();
39489 this.panelSize = {width: w, height: h};
39490 this.activePanel.setSize(w, h);
39492 if(Roo.isIE && this.tabs){
39493 this.tabs.el.repaint();
39498 * Returns the container element for this region.
39499 * @return {Roo.Element}
39501 getEl : function(){
39506 * Hides this region.
39509 //if(!this.collapsed){
39510 this.el.dom.style.left = "-2000px";
39513 // this.collapsedEl.dom.style.left = "-2000px";
39514 // this.collapsedEl.hide();
39516 this.visible = false;
39517 this.fireEvent("visibilitychange", this, false);
39521 * Shows this region if it was previously hidden.
39524 //if(!this.collapsed){
39527 // this.collapsedEl.show();
39529 this.visible = true;
39530 this.fireEvent("visibilitychange", this, true);
39533 closeClicked : function(){
39534 if(this.activePanel){
39535 this.remove(this.activePanel);
39539 collapseClick : function(e){
39541 e.stopPropagation();
39544 e.stopPropagation();
39550 * Collapses this region.
39551 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39554 collapse : function(skipAnim, skipCheck = false){
39555 if(this.collapsed) {
39559 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39561 this.collapsed = true;
39563 this.split.el.hide();
39565 if(this.config.animate && skipAnim !== true){
39566 this.fireEvent("invalidated", this);
39567 this.animateCollapse();
39569 this.el.setLocation(-20000,-20000);
39571 this.collapsedEl.show();
39572 this.fireEvent("collapsed", this);
39573 this.fireEvent("invalidated", this);
39579 animateCollapse : function(){
39584 * Expands this region if it was previously collapsed.
39585 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39586 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39589 expand : function(e, skipAnim){
39591 e.stopPropagation();
39593 if(!this.collapsed || this.el.hasActiveFx()) {
39597 this.afterSlideIn();
39600 this.collapsed = false;
39601 if(this.config.animate && skipAnim !== true){
39602 this.animateExpand();
39606 this.split.el.show();
39608 this.collapsedEl.setLocation(-2000,-2000);
39609 this.collapsedEl.hide();
39610 this.fireEvent("invalidated", this);
39611 this.fireEvent("expanded", this);
39615 animateExpand : function(){
39619 initTabs : function()
39621 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39623 var ts = new Roo.bootstrap.panel.Tabs({
39624 el: this.bodyEl.dom,
39626 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39627 disableTooltips: this.config.disableTabTips,
39628 toolbar : this.config.toolbar
39631 if(this.config.hideTabs){
39632 ts.stripWrap.setDisplayed(false);
39635 ts.resizeTabs = this.config.resizeTabs === true;
39636 ts.minTabWidth = this.config.minTabWidth || 40;
39637 ts.maxTabWidth = this.config.maxTabWidth || 250;
39638 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39639 ts.monitorResize = false;
39640 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39641 ts.bodyEl.addClass('roo-layout-tabs-body');
39642 this.panels.each(this.initPanelAsTab, this);
39645 initPanelAsTab : function(panel){
39646 var ti = this.tabs.addTab(
39650 this.config.closeOnTab && panel.isClosable(),
39653 if(panel.tabTip !== undefined){
39654 ti.setTooltip(panel.tabTip);
39656 ti.on("activate", function(){
39657 this.setActivePanel(panel);
39660 if(this.config.closeOnTab){
39661 ti.on("beforeclose", function(t, e){
39663 this.remove(panel);
39667 panel.tabItem = ti;
39672 updatePanelTitle : function(panel, title)
39674 if(this.activePanel == panel){
39675 this.updateTitle(title);
39678 var ti = this.tabs.getTab(panel.getEl().id);
39680 if(panel.tabTip !== undefined){
39681 ti.setTooltip(panel.tabTip);
39686 updateTitle : function(title){
39687 if(this.titleTextEl && !this.config.title){
39688 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39692 setActivePanel : function(panel)
39694 panel = this.getPanel(panel);
39695 if(this.activePanel && this.activePanel != panel){
39696 if(this.activePanel.setActiveState(false) === false){
39700 this.activePanel = panel;
39701 panel.setActiveState(true);
39702 if(this.panelSize){
39703 panel.setSize(this.panelSize.width, this.panelSize.height);
39706 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39708 this.updateTitle(panel.getTitle());
39710 this.fireEvent("invalidated", this);
39712 this.fireEvent("panelactivated", this, panel);
39716 * Shows the specified panel.
39717 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39718 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39720 showPanel : function(panel)
39722 panel = this.getPanel(panel);
39725 var tab = this.tabs.getTab(panel.getEl().id);
39726 if(tab.isHidden()){
39727 this.tabs.unhideTab(tab.id);
39731 this.setActivePanel(panel);
39738 * Get the active panel for this region.
39739 * @return {Roo.ContentPanel} The active panel or null
39741 getActivePanel : function(){
39742 return this.activePanel;
39745 validateVisibility : function(){
39746 if(this.panels.getCount() < 1){
39747 this.updateTitle(" ");
39748 this.closeBtn.hide();
39751 if(!this.isVisible()){
39758 * Adds the passed ContentPanel(s) to this region.
39759 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39760 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39762 add : function(panel)
39764 if(arguments.length > 1){
39765 for(var i = 0, len = arguments.length; i < len; i++) {
39766 this.add(arguments[i]);
39771 // if we have not been rendered yet, then we can not really do much of this..
39772 if (!this.bodyEl) {
39773 this.unrendered_panels.push(panel);
39780 if(this.hasPanel(panel)){
39781 this.showPanel(panel);
39784 panel.setRegion(this);
39785 this.panels.add(panel);
39786 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39787 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39788 // and hide them... ???
39789 this.bodyEl.dom.appendChild(panel.getEl().dom);
39790 if(panel.background !== true){
39791 this.setActivePanel(panel);
39793 this.fireEvent("paneladded", this, panel);
39800 this.initPanelAsTab(panel);
39804 if(panel.background !== true){
39805 this.tabs.activate(panel.getEl().id);
39807 this.fireEvent("paneladded", this, panel);
39812 * Hides the tab for the specified panel.
39813 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39815 hidePanel : function(panel){
39816 if(this.tabs && (panel = this.getPanel(panel))){
39817 this.tabs.hideTab(panel.getEl().id);
39822 * Unhides the tab for a previously hidden panel.
39823 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39825 unhidePanel : function(panel){
39826 if(this.tabs && (panel = this.getPanel(panel))){
39827 this.tabs.unhideTab(panel.getEl().id);
39831 clearPanels : function(){
39832 while(this.panels.getCount() > 0){
39833 this.remove(this.panels.first());
39838 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39839 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39840 * @param {Boolean} preservePanel Overrides the config preservePanel option
39841 * @return {Roo.ContentPanel} The panel that was removed
39843 remove : function(panel, preservePanel)
39845 panel = this.getPanel(panel);
39850 this.fireEvent("beforeremove", this, panel, e);
39851 if(e.cancel === true){
39854 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39855 var panelId = panel.getId();
39856 this.panels.removeKey(panelId);
39858 document.body.appendChild(panel.getEl().dom);
39861 this.tabs.removeTab(panel.getEl().id);
39862 }else if (!preservePanel){
39863 this.bodyEl.dom.removeChild(panel.getEl().dom);
39865 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39866 var p = this.panels.first();
39867 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39868 tempEl.appendChild(p.getEl().dom);
39869 this.bodyEl.update("");
39870 this.bodyEl.dom.appendChild(p.getEl().dom);
39872 this.updateTitle(p.getTitle());
39874 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39875 this.setActivePanel(p);
39877 panel.setRegion(null);
39878 if(this.activePanel == panel){
39879 this.activePanel = null;
39881 if(this.config.autoDestroy !== false && preservePanel !== true){
39882 try{panel.destroy();}catch(e){}
39884 this.fireEvent("panelremoved", this, panel);
39889 * Returns the TabPanel component used by this region
39890 * @return {Roo.TabPanel}
39892 getTabs : function(){
39896 createTool : function(parentEl, className){
39897 var btn = Roo.DomHelper.append(parentEl, {
39899 cls: "x-layout-tools-button",
39902 cls: "roo-layout-tools-button-inner " + className,
39906 btn.addClassOnOver("roo-layout-tools-button-over");
39911 * Ext JS Library 1.1.1
39912 * Copyright(c) 2006-2007, Ext JS, LLC.
39914 * Originally Released Under LGPL - original licence link has changed is not relivant.
39917 * <script type="text/javascript">
39923 * @class Roo.SplitLayoutRegion
39924 * @extends Roo.LayoutRegion
39925 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39927 Roo.bootstrap.layout.Split = function(config){
39928 this.cursor = config.cursor;
39929 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39932 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39934 splitTip : "Drag to resize.",
39935 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39936 useSplitTips : false,
39938 applyConfig : function(config){
39939 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39942 onRender : function(ctr,pos) {
39944 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39945 if(!this.config.split){
39950 var splitEl = Roo.DomHelper.append(ctr.dom, {
39952 id: this.el.id + "-split",
39953 cls: "roo-layout-split roo-layout-split-"+this.position,
39956 /** The SplitBar for this region
39957 * @type Roo.SplitBar */
39958 // does not exist yet...
39959 Roo.log([this.position, this.orientation]);
39961 this.split = new Roo.bootstrap.SplitBar({
39962 dragElement : splitEl,
39963 resizingElement: this.el,
39964 orientation : this.orientation
39967 this.split.on("moved", this.onSplitMove, this);
39968 this.split.useShim = this.config.useShim === true;
39969 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39970 if(this.useSplitTips){
39971 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39973 //if(config.collapsible){
39974 // this.split.el.on("dblclick", this.collapse, this);
39977 if(typeof this.config.minSize != "undefined"){
39978 this.split.minSize = this.config.minSize;
39980 if(typeof this.config.maxSize != "undefined"){
39981 this.split.maxSize = this.config.maxSize;
39983 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39984 this.hideSplitter();
39989 getHMaxSize : function(){
39990 var cmax = this.config.maxSize || 10000;
39991 var center = this.mgr.getRegion("center");
39992 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39995 getVMaxSize : function(){
39996 var cmax = this.config.maxSize || 10000;
39997 var center = this.mgr.getRegion("center");
39998 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40001 onSplitMove : function(split, newSize){
40002 this.fireEvent("resized", this, newSize);
40006 * Returns the {@link Roo.SplitBar} for this region.
40007 * @return {Roo.SplitBar}
40009 getSplitBar : function(){
40014 this.hideSplitter();
40015 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40018 hideSplitter : function(){
40020 this.split.el.setLocation(-2000,-2000);
40021 this.split.el.hide();
40027 this.split.el.show();
40029 Roo.bootstrap.layout.Split.superclass.show.call(this);
40032 beforeSlide: function(){
40033 if(Roo.isGecko){// firefox overflow auto bug workaround
40034 this.bodyEl.clip();
40036 this.tabs.bodyEl.clip();
40038 if(this.activePanel){
40039 this.activePanel.getEl().clip();
40041 if(this.activePanel.beforeSlide){
40042 this.activePanel.beforeSlide();
40048 afterSlide : function(){
40049 if(Roo.isGecko){// firefox overflow auto bug workaround
40050 this.bodyEl.unclip();
40052 this.tabs.bodyEl.unclip();
40054 if(this.activePanel){
40055 this.activePanel.getEl().unclip();
40056 if(this.activePanel.afterSlide){
40057 this.activePanel.afterSlide();
40063 initAutoHide : function(){
40064 if(this.autoHide !== false){
40065 if(!this.autoHideHd){
40066 var st = new Roo.util.DelayedTask(this.slideIn, this);
40067 this.autoHideHd = {
40068 "mouseout": function(e){
40069 if(!e.within(this.el, true)){
40073 "mouseover" : function(e){
40079 this.el.on(this.autoHideHd);
40083 clearAutoHide : function(){
40084 if(this.autoHide !== false){
40085 this.el.un("mouseout", this.autoHideHd.mouseout);
40086 this.el.un("mouseover", this.autoHideHd.mouseover);
40090 clearMonitor : function(){
40091 Roo.get(document).un("click", this.slideInIf, this);
40094 // these names are backwards but not changed for compat
40095 slideOut : function(){
40096 if(this.isSlid || this.el.hasActiveFx()){
40099 this.isSlid = true;
40100 if(this.collapseBtn){
40101 this.collapseBtn.hide();
40103 this.closeBtnState = this.closeBtn.getStyle('display');
40104 this.closeBtn.hide();
40106 this.stickBtn.show();
40109 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40110 this.beforeSlide();
40111 this.el.setStyle("z-index", 10001);
40112 this.el.slideIn(this.getSlideAnchor(), {
40113 callback: function(){
40115 this.initAutoHide();
40116 Roo.get(document).on("click", this.slideInIf, this);
40117 this.fireEvent("slideshow", this);
40124 afterSlideIn : function(){
40125 this.clearAutoHide();
40126 this.isSlid = false;
40127 this.clearMonitor();
40128 this.el.setStyle("z-index", "");
40129 if(this.collapseBtn){
40130 this.collapseBtn.show();
40132 this.closeBtn.setStyle('display', this.closeBtnState);
40134 this.stickBtn.hide();
40136 this.fireEvent("slidehide", this);
40139 slideIn : function(cb){
40140 if(!this.isSlid || this.el.hasActiveFx()){
40144 this.isSlid = false;
40145 this.beforeSlide();
40146 this.el.slideOut(this.getSlideAnchor(), {
40147 callback: function(){
40148 this.el.setLeftTop(-10000, -10000);
40150 this.afterSlideIn();
40158 slideInIf : function(e){
40159 if(!e.within(this.el)){
40164 animateCollapse : function(){
40165 this.beforeSlide();
40166 this.el.setStyle("z-index", 20000);
40167 var anchor = this.getSlideAnchor();
40168 this.el.slideOut(anchor, {
40169 callback : function(){
40170 this.el.setStyle("z-index", "");
40171 this.collapsedEl.slideIn(anchor, {duration:.3});
40173 this.el.setLocation(-10000,-10000);
40175 this.fireEvent("collapsed", this);
40182 animateExpand : function(){
40183 this.beforeSlide();
40184 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40185 this.el.setStyle("z-index", 20000);
40186 this.collapsedEl.hide({
40189 this.el.slideIn(this.getSlideAnchor(), {
40190 callback : function(){
40191 this.el.setStyle("z-index", "");
40194 this.split.el.show();
40196 this.fireEvent("invalidated", this);
40197 this.fireEvent("expanded", this);
40225 getAnchor : function(){
40226 return this.anchors[this.position];
40229 getCollapseAnchor : function(){
40230 return this.canchors[this.position];
40233 getSlideAnchor : function(){
40234 return this.sanchors[this.position];
40237 getAlignAdj : function(){
40238 var cm = this.cmargins;
40239 switch(this.position){
40255 getExpandAdj : function(){
40256 var c = this.collapsedEl, cm = this.cmargins;
40257 switch(this.position){
40259 return [-(cm.right+c.getWidth()+cm.left), 0];
40262 return [cm.right+c.getWidth()+cm.left, 0];
40265 return [0, -(cm.top+cm.bottom+c.getHeight())];
40268 return [0, cm.top+cm.bottom+c.getHeight()];
40274 * Ext JS Library 1.1.1
40275 * Copyright(c) 2006-2007, Ext JS, LLC.
40277 * Originally Released Under LGPL - original licence link has changed is not relivant.
40280 * <script type="text/javascript">
40283 * These classes are private internal classes
40285 Roo.bootstrap.layout.Center = function(config){
40286 config.region = "center";
40287 Roo.bootstrap.layout.Region.call(this, config);
40288 this.visible = true;
40289 this.minWidth = config.minWidth || 20;
40290 this.minHeight = config.minHeight || 20;
40293 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40295 // center panel can't be hidden
40299 // center panel can't be hidden
40302 getMinWidth: function(){
40303 return this.minWidth;
40306 getMinHeight: function(){
40307 return this.minHeight;
40321 Roo.bootstrap.layout.North = function(config)
40323 config.region = 'north';
40324 config.cursor = 'n-resize';
40326 Roo.bootstrap.layout.Split.call(this, config);
40330 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40331 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40332 this.split.el.addClass("roo-layout-split-v");
40334 //var size = config.initialSize || config.height;
40335 //if(this.el && typeof size != "undefined"){
40336 // this.el.setHeight(size);
40339 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40341 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40344 onRender : function(ctr, pos)
40346 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40347 var size = this.config.initialSize || this.config.height;
40348 if(this.el && typeof size != "undefined"){
40349 this.el.setHeight(size);
40354 getBox : function(){
40355 if(this.collapsed){
40356 return this.collapsedEl.getBox();
40358 var box = this.el.getBox();
40360 box.height += this.split.el.getHeight();
40365 updateBox : function(box){
40366 if(this.split && !this.collapsed){
40367 box.height -= this.split.el.getHeight();
40368 this.split.el.setLeft(box.x);
40369 this.split.el.setTop(box.y+box.height);
40370 this.split.el.setWidth(box.width);
40372 if(this.collapsed){
40373 this.updateBody(box.width, null);
40375 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40383 Roo.bootstrap.layout.South = function(config){
40384 config.region = 'south';
40385 config.cursor = 's-resize';
40386 Roo.bootstrap.layout.Split.call(this, config);
40388 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40389 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40390 this.split.el.addClass("roo-layout-split-v");
40395 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40396 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40398 onRender : function(ctr, pos)
40400 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40401 var size = this.config.initialSize || this.config.height;
40402 if(this.el && typeof size != "undefined"){
40403 this.el.setHeight(size);
40408 getBox : function(){
40409 if(this.collapsed){
40410 return this.collapsedEl.getBox();
40412 var box = this.el.getBox();
40414 var sh = this.split.el.getHeight();
40421 updateBox : function(box){
40422 if(this.split && !this.collapsed){
40423 var sh = this.split.el.getHeight();
40426 this.split.el.setLeft(box.x);
40427 this.split.el.setTop(box.y-sh);
40428 this.split.el.setWidth(box.width);
40430 if(this.collapsed){
40431 this.updateBody(box.width, null);
40433 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40437 Roo.bootstrap.layout.East = function(config){
40438 config.region = "east";
40439 config.cursor = "e-resize";
40440 Roo.bootstrap.layout.Split.call(this, config);
40442 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40443 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40444 this.split.el.addClass("roo-layout-split-h");
40448 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40449 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40451 onRender : function(ctr, pos)
40453 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40454 var size = this.config.initialSize || this.config.width;
40455 if(this.el && typeof size != "undefined"){
40456 this.el.setWidth(size);
40461 getBox : function(){
40462 if(this.collapsed){
40463 return this.collapsedEl.getBox();
40465 var box = this.el.getBox();
40467 var sw = this.split.el.getWidth();
40474 updateBox : function(box){
40475 if(this.split && !this.collapsed){
40476 var sw = this.split.el.getWidth();
40478 this.split.el.setLeft(box.x);
40479 this.split.el.setTop(box.y);
40480 this.split.el.setHeight(box.height);
40483 if(this.collapsed){
40484 this.updateBody(null, box.height);
40486 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40490 Roo.bootstrap.layout.West = function(config){
40491 config.region = "west";
40492 config.cursor = "w-resize";
40494 Roo.bootstrap.layout.Split.call(this, config);
40496 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40497 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40498 this.split.el.addClass("roo-layout-split-h");
40502 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40503 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40505 onRender: function(ctr, pos)
40507 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40508 var size = this.config.initialSize || this.config.width;
40509 if(typeof size != "undefined"){
40510 this.el.setWidth(size);
40514 getBox : function(){
40515 if(this.collapsed){
40516 return this.collapsedEl.getBox();
40518 var box = this.el.getBox();
40519 if (box.width == 0) {
40520 box.width = this.config.width; // kludge?
40523 box.width += this.split.el.getWidth();
40528 updateBox : function(box){
40529 if(this.split && !this.collapsed){
40530 var sw = this.split.el.getWidth();
40532 this.split.el.setLeft(box.x+box.width);
40533 this.split.el.setTop(box.y);
40534 this.split.el.setHeight(box.height);
40536 if(this.collapsed){
40537 this.updateBody(null, box.height);
40539 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40541 });Roo.namespace("Roo.bootstrap.panel");/*
40543 * Ext JS Library 1.1.1
40544 * Copyright(c) 2006-2007, Ext JS, LLC.
40546 * Originally Released Under LGPL - original licence link has changed is not relivant.
40549 * <script type="text/javascript">
40552 * @class Roo.ContentPanel
40553 * @extends Roo.util.Observable
40554 * A basic ContentPanel element.
40555 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40556 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40557 * @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
40558 * @cfg {Boolean} closable True if the panel can be closed/removed
40559 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40560 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40561 * @cfg {Toolbar} toolbar A toolbar for this panel
40562 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40563 * @cfg {String} title The title for this panel
40564 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40565 * @cfg {String} url Calls {@link #setUrl} with this value
40566 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40567 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40568 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40569 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40570 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40571 * @cfg {Boolean} badges render the badges
40572 * @cfg {String} cls extra classes to use
40573 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40576 * Create a new ContentPanel.
40577 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40578 * @param {String/Object} config A string to set only the title or a config object
40579 * @param {String} content (optional) Set the HTML content for this panel
40580 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40582 Roo.bootstrap.panel.Content = function( config){
40584 this.tpl = config.tpl || false;
40586 var el = config.el;
40587 var content = config.content;
40589 if(config.autoCreate){ // xtype is available if this is called from factory
40592 this.el = Roo.get(el);
40593 if(!this.el && config && config.autoCreate){
40594 if(typeof config.autoCreate == "object"){
40595 if(!config.autoCreate.id){
40596 config.autoCreate.id = config.id||el;
40598 this.el = Roo.DomHelper.append(document.body,
40599 config.autoCreate, true);
40603 cls: (config.cls || '') +
40604 (config.background ? ' bg-' + config.background : '') +
40605 " roo-layout-inactive-content",
40608 if (config.iframe) {
40612 style : 'border: 0px',
40613 src : 'about:blank'
40619 elcfg.html = config.html;
40623 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40624 if (config.iframe) {
40625 this.iframeEl = this.el.select('iframe',true).first();
40630 this.closable = false;
40631 this.loaded = false;
40632 this.active = false;
40635 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40637 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40639 this.wrapEl = this.el; //this.el.wrap();
40641 if (config.toolbar.items) {
40642 ti = config.toolbar.items ;
40643 delete config.toolbar.items ;
40647 this.toolbar.render(this.wrapEl, 'before');
40648 for(var i =0;i < ti.length;i++) {
40649 // Roo.log(['add child', items[i]]);
40650 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40652 this.toolbar.items = nitems;
40653 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40654 delete config.toolbar;
40658 // xtype created footer. - not sure if will work as we normally have to render first..
40659 if (this.footer && !this.footer.el && this.footer.xtype) {
40660 if (!this.wrapEl) {
40661 this.wrapEl = this.el.wrap();
40664 this.footer.container = this.wrapEl.createChild();
40666 this.footer = Roo.factory(this.footer, Roo);
40671 if(typeof config == "string"){
40672 this.title = config;
40674 Roo.apply(this, config);
40678 this.resizeEl = Roo.get(this.resizeEl, true);
40680 this.resizeEl = this.el;
40682 // handle view.xtype
40690 * Fires when this panel is activated.
40691 * @param {Roo.ContentPanel} this
40695 * @event deactivate
40696 * Fires when this panel is activated.
40697 * @param {Roo.ContentPanel} this
40699 "deactivate" : true,
40703 * Fires when this panel is resized if fitToFrame is true.
40704 * @param {Roo.ContentPanel} this
40705 * @param {Number} width The width after any component adjustments
40706 * @param {Number} height The height after any component adjustments
40712 * Fires when this tab is created
40713 * @param {Roo.ContentPanel} this
40719 * Fires when this content is scrolled
40720 * @param {Roo.ContentPanel} this
40721 * @param {Event} scrollEvent
40732 if(this.autoScroll && !this.iframe){
40733 this.resizeEl.setStyle("overflow", "auto");
40734 this.resizeEl.on('scroll', this.onScroll, this);
40736 // fix randome scrolling
40737 //this.el.on('scroll', function() {
40738 // Roo.log('fix random scolling');
40739 // this.scrollTo('top',0);
40742 content = content || this.content;
40744 this.setContent(content);
40746 if(config && config.url){
40747 this.setUrl(this.url, this.params, this.loadOnce);
40752 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40754 if (this.view && typeof(this.view.xtype) != 'undefined') {
40755 this.view.el = this.el.appendChild(document.createElement("div"));
40756 this.view = Roo.factory(this.view);
40757 this.view.render && this.view.render(false, '');
40761 this.fireEvent('render', this);
40764 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40774 /* Resize Element - use this to work out scroll etc. */
40777 setRegion : function(region){
40778 this.region = region;
40779 this.setActiveClass(region && !this.background);
40783 setActiveClass: function(state)
40786 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40787 this.el.setStyle('position','relative');
40789 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40790 this.el.setStyle('position', 'absolute');
40795 * Returns the toolbar for this Panel if one was configured.
40796 * @return {Roo.Toolbar}
40798 getToolbar : function(){
40799 return this.toolbar;
40802 setActiveState : function(active)
40804 this.active = active;
40805 this.setActiveClass(active);
40807 if(this.fireEvent("deactivate", this) === false){
40812 this.fireEvent("activate", this);
40816 * Updates this panel's element (not for iframe)
40817 * @param {String} content The new content
40818 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40820 setContent : function(content, loadScripts){
40825 this.el.update(content, loadScripts);
40828 ignoreResize : function(w, h){
40829 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40832 this.lastSize = {width: w, height: h};
40837 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40838 * @return {Roo.UpdateManager} The UpdateManager
40840 getUpdateManager : function(){
40844 return this.el.getUpdateManager();
40847 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40848 * Does not work with IFRAME contents
40849 * @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:
40852 url: "your-url.php",
40853 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40854 callback: yourFunction,
40855 scope: yourObject, //(optional scope)
40858 text: "Loading...",
40864 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40865 * 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.
40866 * @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}
40867 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40868 * @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.
40869 * @return {Roo.ContentPanel} this
40877 var um = this.el.getUpdateManager();
40878 um.update.apply(um, arguments);
40884 * 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.
40885 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40886 * @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)
40887 * @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)
40888 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40890 setUrl : function(url, params, loadOnce){
40892 this.iframeEl.dom.src = url;
40896 if(this.refreshDelegate){
40897 this.removeListener("activate", this.refreshDelegate);
40899 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40900 this.on("activate", this.refreshDelegate);
40901 return this.el.getUpdateManager();
40904 _handleRefresh : function(url, params, loadOnce){
40905 if(!loadOnce || !this.loaded){
40906 var updater = this.el.getUpdateManager();
40907 updater.update(url, params, this._setLoaded.createDelegate(this));
40911 _setLoaded : function(){
40912 this.loaded = true;
40916 * Returns this panel's id
40919 getId : function(){
40924 * Returns this panel's element - used by regiosn to add.
40925 * @return {Roo.Element}
40927 getEl : function(){
40928 return this.wrapEl || this.el;
40933 adjustForComponents : function(width, height)
40935 //Roo.log('adjustForComponents ');
40936 if(this.resizeEl != this.el){
40937 width -= this.el.getFrameWidth('lr');
40938 height -= this.el.getFrameWidth('tb');
40941 var te = this.toolbar.getEl();
40942 te.setWidth(width);
40943 height -= te.getHeight();
40946 var te = this.footer.getEl();
40947 te.setWidth(width);
40948 height -= te.getHeight();
40952 if(this.adjustments){
40953 width += this.adjustments[0];
40954 height += this.adjustments[1];
40956 return {"width": width, "height": height};
40959 setSize : function(width, height){
40960 if(this.fitToFrame && !this.ignoreResize(width, height)){
40961 if(this.fitContainer && this.resizeEl != this.el){
40962 this.el.setSize(width, height);
40964 var size = this.adjustForComponents(width, height);
40966 this.iframeEl.setSize(width,height);
40969 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40970 this.fireEvent('resize', this, size.width, size.height);
40977 * Returns this panel's title
40980 getTitle : function(){
40982 if (typeof(this.title) != 'object') {
40987 for (var k in this.title) {
40988 if (!this.title.hasOwnProperty(k)) {
40992 if (k.indexOf('-') >= 0) {
40993 var s = k.split('-');
40994 for (var i = 0; i<s.length; i++) {
40995 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40998 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41005 * Set this panel's title
41006 * @param {String} title
41008 setTitle : function(title){
41009 this.title = title;
41011 this.region.updatePanelTitle(this, title);
41016 * Returns true is this panel was configured to be closable
41017 * @return {Boolean}
41019 isClosable : function(){
41020 return this.closable;
41023 beforeSlide : function(){
41025 this.resizeEl.clip();
41028 afterSlide : function(){
41030 this.resizeEl.unclip();
41034 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41035 * Will fail silently if the {@link #setUrl} method has not been called.
41036 * This does not activate the panel, just updates its content.
41038 refresh : function(){
41039 if(this.refreshDelegate){
41040 this.loaded = false;
41041 this.refreshDelegate();
41046 * Destroys this panel
41048 destroy : function(){
41049 this.el.removeAllListeners();
41050 var tempEl = document.createElement("span");
41051 tempEl.appendChild(this.el.dom);
41052 tempEl.innerHTML = "";
41058 * form - if the content panel contains a form - this is a reference to it.
41059 * @type {Roo.form.Form}
41063 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41064 * This contains a reference to it.
41070 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41080 * @param {Object} cfg Xtype definition of item to add.
41084 getChildContainer: function () {
41085 return this.getEl();
41089 onScroll : function(e)
41091 this.fireEvent('scroll', this, e);
41096 var ret = new Roo.factory(cfg);
41101 if (cfg.xtype.match(/^Form$/)) {
41104 //if (this.footer) {
41105 // el = this.footer.container.insertSibling(false, 'before');
41107 el = this.el.createChild();
41110 this.form = new Roo.form.Form(cfg);
41113 if ( this.form.allItems.length) {
41114 this.form.render(el.dom);
41118 // should only have one of theses..
41119 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41120 // views.. should not be just added - used named prop 'view''
41122 cfg.el = this.el.appendChild(document.createElement("div"));
41125 var ret = new Roo.factory(cfg);
41127 ret.render && ret.render(false, ''); // render blank..
41137 * @class Roo.bootstrap.panel.Grid
41138 * @extends Roo.bootstrap.panel.Content
41140 * Create a new GridPanel.
41141 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41142 * @param {Object} config A the config object
41148 Roo.bootstrap.panel.Grid = function(config)
41152 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41153 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41155 config.el = this.wrapper;
41156 //this.el = this.wrapper;
41158 if (config.container) {
41159 // ctor'ed from a Border/panel.grid
41162 this.wrapper.setStyle("overflow", "hidden");
41163 this.wrapper.addClass('roo-grid-container');
41168 if(config.toolbar){
41169 var tool_el = this.wrapper.createChild();
41170 this.toolbar = Roo.factory(config.toolbar);
41172 if (config.toolbar.items) {
41173 ti = config.toolbar.items ;
41174 delete config.toolbar.items ;
41178 this.toolbar.render(tool_el);
41179 for(var i =0;i < ti.length;i++) {
41180 // Roo.log(['add child', items[i]]);
41181 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41183 this.toolbar.items = nitems;
41185 delete config.toolbar;
41188 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41189 config.grid.scrollBody = true;;
41190 config.grid.monitorWindowResize = false; // turn off autosizing
41191 config.grid.autoHeight = false;
41192 config.grid.autoWidth = false;
41194 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41196 if (config.background) {
41197 // render grid on panel activation (if panel background)
41198 this.on('activate', function(gp) {
41199 if (!gp.grid.rendered) {
41200 gp.grid.render(this.wrapper);
41201 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41206 this.grid.render(this.wrapper);
41207 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41210 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41211 // ??? needed ??? config.el = this.wrapper;
41216 // xtype created footer. - not sure if will work as we normally have to render first..
41217 if (this.footer && !this.footer.el && this.footer.xtype) {
41219 var ctr = this.grid.getView().getFooterPanel(true);
41220 this.footer.dataSource = this.grid.dataSource;
41221 this.footer = Roo.factory(this.footer, Roo);
41222 this.footer.render(ctr);
41232 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41233 getId : function(){
41234 return this.grid.id;
41238 * Returns the grid for this panel
41239 * @return {Roo.bootstrap.Table}
41241 getGrid : function(){
41245 setSize : function(width, height){
41246 if(!this.ignoreResize(width, height)){
41247 var grid = this.grid;
41248 var size = this.adjustForComponents(width, height);
41249 // tfoot is not a footer?
41252 var gridel = grid.getGridEl();
41253 gridel.setSize(size.width, size.height);
41255 var tbd = grid.getGridEl().select('tbody', true).first();
41256 var thd = grid.getGridEl().select('thead',true).first();
41257 var tbf= grid.getGridEl().select('tfoot', true).first();
41260 size.height -= tbf.getHeight();
41263 size.height -= thd.getHeight();
41266 tbd.setSize(size.width, size.height );
41267 // this is for the account management tab -seems to work there.
41268 var thd = grid.getGridEl().select('thead',true).first();
41270 // tbd.setSize(size.width, size.height - thd.getHeight());
41279 beforeSlide : function(){
41280 this.grid.getView().scroller.clip();
41283 afterSlide : function(){
41284 this.grid.getView().scroller.unclip();
41287 destroy : function(){
41288 this.grid.destroy();
41290 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41295 * @class Roo.bootstrap.panel.Nest
41296 * @extends Roo.bootstrap.panel.Content
41298 * Create a new Panel, that can contain a layout.Border.
41301 * @param {Roo.BorderLayout} layout The layout for this panel
41302 * @param {String/Object} config A string to set only the title or a config object
41304 Roo.bootstrap.panel.Nest = function(config)
41306 // construct with only one argument..
41307 /* FIXME - implement nicer consturctors
41308 if (layout.layout) {
41310 layout = config.layout;
41311 delete config.layout;
41313 if (layout.xtype && !layout.getEl) {
41314 // then layout needs constructing..
41315 layout = Roo.factory(layout, Roo);
41319 config.el = config.layout.getEl();
41321 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41323 config.layout.monitorWindowResize = false; // turn off autosizing
41324 this.layout = config.layout;
41325 this.layout.getEl().addClass("roo-layout-nested-layout");
41326 this.layout.parent = this;
41333 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41335 setSize : function(width, height){
41336 if(!this.ignoreResize(width, height)){
41337 var size = this.adjustForComponents(width, height);
41338 var el = this.layout.getEl();
41339 if (size.height < 1) {
41340 el.setWidth(size.width);
41342 el.setSize(size.width, size.height);
41344 var touch = el.dom.offsetWidth;
41345 this.layout.layout();
41346 // ie requires a double layout on the first pass
41347 if(Roo.isIE && !this.initialized){
41348 this.initialized = true;
41349 this.layout.layout();
41354 // activate all subpanels if not currently active..
41356 setActiveState : function(active){
41357 this.active = active;
41358 this.setActiveClass(active);
41361 this.fireEvent("deactivate", this);
41365 this.fireEvent("activate", this);
41366 // not sure if this should happen before or after..
41367 if (!this.layout) {
41368 return; // should not happen..
41371 for (var r in this.layout.regions) {
41372 reg = this.layout.getRegion(r);
41373 if (reg.getActivePanel()) {
41374 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41375 reg.setActivePanel(reg.getActivePanel());
41378 if (!reg.panels.length) {
41381 reg.showPanel(reg.getPanel(0));
41390 * Returns the nested BorderLayout for this panel
41391 * @return {Roo.BorderLayout}
41393 getLayout : function(){
41394 return this.layout;
41398 * Adds a xtype elements to the layout of the nested panel
41402 xtype : 'ContentPanel',
41409 xtype : 'NestedLayoutPanel',
41415 items : [ ... list of content panels or nested layout panels.. ]
41419 * @param {Object} cfg Xtype definition of item to add.
41421 addxtype : function(cfg) {
41422 return this.layout.addxtype(cfg);
41427 * Ext JS Library 1.1.1
41428 * Copyright(c) 2006-2007, Ext JS, LLC.
41430 * Originally Released Under LGPL - original licence link has changed is not relivant.
41433 * <script type="text/javascript">
41436 * @class Roo.TabPanel
41437 * @extends Roo.util.Observable
41438 * A lightweight tab container.
41442 // basic tabs 1, built from existing content
41443 var tabs = new Roo.TabPanel("tabs1");
41444 tabs.addTab("script", "View Script");
41445 tabs.addTab("markup", "View Markup");
41446 tabs.activate("script");
41448 // more advanced tabs, built from javascript
41449 var jtabs = new Roo.TabPanel("jtabs");
41450 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41452 // set up the UpdateManager
41453 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41454 var updater = tab2.getUpdateManager();
41455 updater.setDefaultUrl("ajax1.htm");
41456 tab2.on('activate', updater.refresh, updater, true);
41458 // Use setUrl for Ajax loading
41459 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41460 tab3.setUrl("ajax2.htm", null, true);
41463 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41466 jtabs.activate("jtabs-1");
41469 * Create a new TabPanel.
41470 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41471 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41473 Roo.bootstrap.panel.Tabs = function(config){
41475 * The container element for this TabPanel.
41476 * @type Roo.Element
41478 this.el = Roo.get(config.el);
41481 if(typeof config == "boolean"){
41482 this.tabPosition = config ? "bottom" : "top";
41484 Roo.apply(this, config);
41488 if(this.tabPosition == "bottom"){
41489 // if tabs are at the bottom = create the body first.
41490 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41491 this.el.addClass("roo-tabs-bottom");
41493 // next create the tabs holders
41495 if (this.tabPosition == "west"){
41497 var reg = this.region; // fake it..
41499 if (!reg.mgr.parent) {
41502 reg = reg.mgr.parent.region;
41504 Roo.log("got nest?");
41506 if (reg.mgr.getRegion('west')) {
41507 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41508 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41509 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41510 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41511 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41519 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41520 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41521 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41522 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41527 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41530 // finally - if tabs are at the top, then create the body last..
41531 if(this.tabPosition != "bottom"){
41532 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41533 * @type Roo.Element
41535 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41536 this.el.addClass("roo-tabs-top");
41540 this.bodyEl.setStyle("position", "relative");
41542 this.active = null;
41543 this.activateDelegate = this.activate.createDelegate(this);
41548 * Fires when the active tab changes
41549 * @param {Roo.TabPanel} this
41550 * @param {Roo.TabPanelItem} activePanel The new active tab
41554 * @event beforetabchange
41555 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41556 * @param {Roo.TabPanel} this
41557 * @param {Object} e Set cancel to true on this object to cancel the tab change
41558 * @param {Roo.TabPanelItem} tab The tab being changed to
41560 "beforetabchange" : true
41563 Roo.EventManager.onWindowResize(this.onResize, this);
41564 this.cpad = this.el.getPadding("lr");
41565 this.hiddenCount = 0;
41568 // toolbar on the tabbar support...
41569 if (this.toolbar) {
41570 alert("no toolbar support yet");
41571 this.toolbar = false;
41573 var tcfg = this.toolbar;
41574 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41575 this.toolbar = new Roo.Toolbar(tcfg);
41576 if (Roo.isSafari) {
41577 var tbl = tcfg.container.child('table', true);
41578 tbl.setAttribute('width', '100%');
41586 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41589 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41591 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41593 tabPosition : "top",
41595 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41597 currentTabWidth : 0,
41599 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41603 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41607 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41609 preferredTabWidth : 175,
41611 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41613 resizeTabs : false,
41615 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41617 monitorResize : true,
41619 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41621 toolbar : false, // set by caller..
41623 region : false, /// set by caller
41625 disableTooltips : true, // not used yet...
41628 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41629 * @param {String} id The id of the div to use <b>or create</b>
41630 * @param {String} text The text for the tab
41631 * @param {String} content (optional) Content to put in the TabPanelItem body
41632 * @param {Boolean} closable (optional) True to create a close icon on the tab
41633 * @return {Roo.TabPanelItem} The created TabPanelItem
41635 addTab : function(id, text, content, closable, tpl)
41637 var item = new Roo.bootstrap.panel.TabItem({
41641 closable : closable,
41644 this.addTabItem(item);
41646 item.setContent(content);
41652 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41653 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41654 * @return {Roo.TabPanelItem}
41656 getTab : function(id){
41657 return this.items[id];
41661 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41662 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41664 hideTab : function(id){
41665 var t = this.items[id];
41668 this.hiddenCount++;
41669 this.autoSizeTabs();
41674 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41675 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41677 unhideTab : function(id){
41678 var t = this.items[id];
41680 t.setHidden(false);
41681 this.hiddenCount--;
41682 this.autoSizeTabs();
41687 * Adds an existing {@link Roo.TabPanelItem}.
41688 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41690 addTabItem : function(item)
41692 this.items[item.id] = item;
41693 this.items.push(item);
41694 this.autoSizeTabs();
41695 // if(this.resizeTabs){
41696 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41697 // this.autoSizeTabs();
41699 // item.autoSize();
41704 * Removes a {@link Roo.TabPanelItem}.
41705 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41707 removeTab : function(id){
41708 var items = this.items;
41709 var tab = items[id];
41710 if(!tab) { return; }
41711 var index = items.indexOf(tab);
41712 if(this.active == tab && items.length > 1){
41713 var newTab = this.getNextAvailable(index);
41718 this.stripEl.dom.removeChild(tab.pnode.dom);
41719 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41720 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41722 items.splice(index, 1);
41723 delete this.items[tab.id];
41724 tab.fireEvent("close", tab);
41725 tab.purgeListeners();
41726 this.autoSizeTabs();
41729 getNextAvailable : function(start){
41730 var items = this.items;
41732 // look for a next tab that will slide over to
41733 // replace the one being removed
41734 while(index < items.length){
41735 var item = items[++index];
41736 if(item && !item.isHidden()){
41740 // if one isn't found select the previous tab (on the left)
41743 var item = items[--index];
41744 if(item && !item.isHidden()){
41752 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41753 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41755 disableTab : function(id){
41756 var tab = this.items[id];
41757 if(tab && this.active != tab){
41763 * Enables a {@link Roo.TabPanelItem} that is disabled.
41764 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41766 enableTab : function(id){
41767 var tab = this.items[id];
41772 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41773 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41774 * @return {Roo.TabPanelItem} The TabPanelItem.
41776 activate : function(id)
41778 //Roo.log('activite:' + id);
41780 var tab = this.items[id];
41784 if(tab == this.active || tab.disabled){
41788 this.fireEvent("beforetabchange", this, e, tab);
41789 if(e.cancel !== true && !tab.disabled){
41791 this.active.hide();
41793 this.active = this.items[id];
41794 this.active.show();
41795 this.fireEvent("tabchange", this, this.active);
41801 * Gets the active {@link Roo.TabPanelItem}.
41802 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41804 getActiveTab : function(){
41805 return this.active;
41809 * Updates the tab body element to fit the height of the container element
41810 * for overflow scrolling
41811 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41813 syncHeight : function(targetHeight){
41814 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41815 var bm = this.bodyEl.getMargins();
41816 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41817 this.bodyEl.setHeight(newHeight);
41821 onResize : function(){
41822 if(this.monitorResize){
41823 this.autoSizeTabs();
41828 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41830 beginUpdate : function(){
41831 this.updating = true;
41835 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41837 endUpdate : function(){
41838 this.updating = false;
41839 this.autoSizeTabs();
41843 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41845 autoSizeTabs : function()
41847 var count = this.items.length;
41848 var vcount = count - this.hiddenCount;
41851 this.stripEl.hide();
41853 this.stripEl.show();
41856 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41861 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41862 var availWidth = Math.floor(w / vcount);
41863 var b = this.stripBody;
41864 if(b.getWidth() > w){
41865 var tabs = this.items;
41866 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41867 if(availWidth < this.minTabWidth){
41868 /*if(!this.sleft){ // incomplete scrolling code
41869 this.createScrollButtons();
41872 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41875 if(this.currentTabWidth < this.preferredTabWidth){
41876 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41882 * Returns the number of tabs in this TabPanel.
41885 getCount : function(){
41886 return this.items.length;
41890 * Resizes all the tabs to the passed width
41891 * @param {Number} The new width
41893 setTabWidth : function(width){
41894 this.currentTabWidth = width;
41895 for(var i = 0, len = this.items.length; i < len; i++) {
41896 if(!this.items[i].isHidden()) {
41897 this.items[i].setWidth(width);
41903 * Destroys this TabPanel
41904 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41906 destroy : function(removeEl){
41907 Roo.EventManager.removeResizeListener(this.onResize, this);
41908 for(var i = 0, len = this.items.length; i < len; i++){
41909 this.items[i].purgeListeners();
41911 if(removeEl === true){
41912 this.el.update("");
41917 createStrip : function(container)
41919 var strip = document.createElement("nav");
41920 strip.className = Roo.bootstrap.version == 4 ?
41921 "navbar-light bg-light" :
41922 "navbar navbar-default"; //"x-tabs-wrap";
41923 container.appendChild(strip);
41927 createStripList : function(strip)
41929 // div wrapper for retard IE
41930 // returns the "tr" element.
41931 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41932 //'<div class="x-tabs-strip-wrap">'+
41933 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41934 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41935 return strip.firstChild; //.firstChild.firstChild.firstChild;
41937 createBody : function(container)
41939 var body = document.createElement("div");
41940 Roo.id(body, "tab-body");
41941 //Roo.fly(body).addClass("x-tabs-body");
41942 Roo.fly(body).addClass("tab-content");
41943 container.appendChild(body);
41946 createItemBody :function(bodyEl, id){
41947 var body = Roo.getDom(id);
41949 body = document.createElement("div");
41952 //Roo.fly(body).addClass("x-tabs-item-body");
41953 Roo.fly(body).addClass("tab-pane");
41954 bodyEl.insertBefore(body, bodyEl.firstChild);
41958 createStripElements : function(stripEl, text, closable, tpl)
41960 var td = document.createElement("li"); // was td..
41961 td.className = 'nav-item';
41963 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41966 stripEl.appendChild(td);
41968 td.className = "x-tabs-closable";
41969 if(!this.closeTpl){
41970 this.closeTpl = new Roo.Template(
41971 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41972 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41973 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41976 var el = this.closeTpl.overwrite(td, {"text": text});
41977 var close = el.getElementsByTagName("div")[0];
41978 var inner = el.getElementsByTagName("em")[0];
41979 return {"el": el, "close": close, "inner": inner};
41982 // not sure what this is..
41983 // if(!this.tabTpl){
41984 //this.tabTpl = new Roo.Template(
41985 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41986 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41988 // this.tabTpl = new Roo.Template(
41989 // '<a href="#">' +
41990 // '<span unselectable="on"' +
41991 // (this.disableTooltips ? '' : ' title="{text}"') +
41992 // ' >{text}</span></a>'
41998 var template = tpl || this.tabTpl || false;
42001 template = new Roo.Template(
42002 Roo.bootstrap.version == 4 ?
42004 '<a class="nav-link" href="#" unselectable="on"' +
42005 (this.disableTooltips ? '' : ' title="{text}"') +
42008 '<a class="nav-link" href="#">' +
42009 '<span unselectable="on"' +
42010 (this.disableTooltips ? '' : ' title="{text}"') +
42011 ' >{text}</span></a>'
42016 switch (typeof(template)) {
42020 template = new Roo.Template(template);
42026 var el = template.overwrite(td, {"text": text});
42028 var inner = el.getElementsByTagName("span")[0];
42030 return {"el": el, "inner": inner};
42038 * @class Roo.TabPanelItem
42039 * @extends Roo.util.Observable
42040 * Represents an individual item (tab plus body) in a TabPanel.
42041 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42042 * @param {String} id The id of this TabPanelItem
42043 * @param {String} text The text for the tab of this TabPanelItem
42044 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42046 Roo.bootstrap.panel.TabItem = function(config){
42048 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42049 * @type Roo.TabPanel
42051 this.tabPanel = config.panel;
42053 * The id for this TabPanelItem
42056 this.id = config.id;
42058 this.disabled = false;
42060 this.text = config.text;
42062 this.loaded = false;
42063 this.closable = config.closable;
42066 * The body element for this TabPanelItem.
42067 * @type Roo.Element
42069 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42070 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42071 this.bodyEl.setStyle("display", "block");
42072 this.bodyEl.setStyle("zoom", "1");
42073 //this.hideAction();
42075 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42077 this.el = Roo.get(els.el);
42078 this.inner = Roo.get(els.inner, true);
42079 this.textEl = Roo.bootstrap.version == 4 ?
42080 this.el : Roo.get(this.el.dom.firstChild, true);
42082 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42083 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42086 // this.el.on("mousedown", this.onTabMouseDown, this);
42087 this.el.on("click", this.onTabClick, this);
42089 if(config.closable){
42090 var c = Roo.get(els.close, true);
42091 c.dom.title = this.closeText;
42092 c.addClassOnOver("close-over");
42093 c.on("click", this.closeClick, this);
42099 * Fires when this tab becomes the active tab.
42100 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42101 * @param {Roo.TabPanelItem} this
42105 * @event beforeclose
42106 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42107 * @param {Roo.TabPanelItem} this
42108 * @param {Object} e Set cancel to true on this object to cancel the close.
42110 "beforeclose": true,
42113 * Fires when this tab is closed.
42114 * @param {Roo.TabPanelItem} this
42118 * @event deactivate
42119 * Fires when this tab is no longer the active tab.
42120 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42121 * @param {Roo.TabPanelItem} this
42123 "deactivate" : true
42125 this.hidden = false;
42127 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42130 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42132 purgeListeners : function(){
42133 Roo.util.Observable.prototype.purgeListeners.call(this);
42134 this.el.removeAllListeners();
42137 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42140 this.status_node.addClass("active");
42143 this.tabPanel.stripWrap.repaint();
42145 this.fireEvent("activate", this.tabPanel, this);
42149 * Returns true if this tab is the active tab.
42150 * @return {Boolean}
42152 isActive : function(){
42153 return this.tabPanel.getActiveTab() == this;
42157 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42160 this.status_node.removeClass("active");
42162 this.fireEvent("deactivate", this.tabPanel, this);
42165 hideAction : function(){
42166 this.bodyEl.hide();
42167 this.bodyEl.setStyle("position", "absolute");
42168 this.bodyEl.setLeft("-20000px");
42169 this.bodyEl.setTop("-20000px");
42172 showAction : function(){
42173 this.bodyEl.setStyle("position", "relative");
42174 this.bodyEl.setTop("");
42175 this.bodyEl.setLeft("");
42176 this.bodyEl.show();
42180 * Set the tooltip for the tab.
42181 * @param {String} tooltip The tab's tooltip
42183 setTooltip : function(text){
42184 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42185 this.textEl.dom.qtip = text;
42186 this.textEl.dom.removeAttribute('title');
42188 this.textEl.dom.title = text;
42192 onTabClick : function(e){
42193 e.preventDefault();
42194 this.tabPanel.activate(this.id);
42197 onTabMouseDown : function(e){
42198 e.preventDefault();
42199 this.tabPanel.activate(this.id);
42202 getWidth : function(){
42203 return this.inner.getWidth();
42206 setWidth : function(width){
42207 var iwidth = width - this.linode.getPadding("lr");
42208 this.inner.setWidth(iwidth);
42209 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42210 this.linode.setWidth(width);
42214 * Show or hide the tab
42215 * @param {Boolean} hidden True to hide or false to show.
42217 setHidden : function(hidden){
42218 this.hidden = hidden;
42219 this.linode.setStyle("display", hidden ? "none" : "");
42223 * Returns true if this tab is "hidden"
42224 * @return {Boolean}
42226 isHidden : function(){
42227 return this.hidden;
42231 * Returns the text for this tab
42234 getText : function(){
42238 autoSize : function(){
42239 //this.el.beginMeasure();
42240 this.textEl.setWidth(1);
42242 * #2804 [new] Tabs in Roojs
42243 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42245 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42246 //this.el.endMeasure();
42250 * Sets the text for the tab (Note: this also sets the tooltip text)
42251 * @param {String} text The tab's text and tooltip
42253 setText : function(text){
42255 this.textEl.update(text);
42256 this.setTooltip(text);
42257 //if(!this.tabPanel.resizeTabs){
42258 // this.autoSize();
42262 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42264 activate : function(){
42265 this.tabPanel.activate(this.id);
42269 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42271 disable : function(){
42272 if(this.tabPanel.active != this){
42273 this.disabled = true;
42274 this.status_node.addClass("disabled");
42279 * Enables this TabPanelItem if it was previously disabled.
42281 enable : function(){
42282 this.disabled = false;
42283 this.status_node.removeClass("disabled");
42287 * Sets the content for this TabPanelItem.
42288 * @param {String} content The content
42289 * @param {Boolean} loadScripts true to look for and load scripts
42291 setContent : function(content, loadScripts){
42292 this.bodyEl.update(content, loadScripts);
42296 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42297 * @return {Roo.UpdateManager} The UpdateManager
42299 getUpdateManager : function(){
42300 return this.bodyEl.getUpdateManager();
42304 * Set a URL to be used to load the content for this TabPanelItem.
42305 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42306 * @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)
42307 * @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)
42308 * @return {Roo.UpdateManager} The UpdateManager
42310 setUrl : function(url, params, loadOnce){
42311 if(this.refreshDelegate){
42312 this.un('activate', this.refreshDelegate);
42314 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42315 this.on("activate", this.refreshDelegate);
42316 return this.bodyEl.getUpdateManager();
42320 _handleRefresh : function(url, params, loadOnce){
42321 if(!loadOnce || !this.loaded){
42322 var updater = this.bodyEl.getUpdateManager();
42323 updater.update(url, params, this._setLoaded.createDelegate(this));
42328 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42329 * Will fail silently if the setUrl method has not been called.
42330 * This does not activate the panel, just updates its content.
42332 refresh : function(){
42333 if(this.refreshDelegate){
42334 this.loaded = false;
42335 this.refreshDelegate();
42340 _setLoaded : function(){
42341 this.loaded = true;
42345 closeClick : function(e){
42348 this.fireEvent("beforeclose", this, o);
42349 if(o.cancel !== true){
42350 this.tabPanel.removeTab(this.id);
42354 * The text displayed in the tooltip for the close icon.
42357 closeText : "Close this tab"
42360 * This script refer to:
42361 * Title: International Telephone Input
42362 * Author: Jack O'Connor
42363 * Code version: v12.1.12
42364 * Availability: https://github.com/jackocnr/intl-tel-input.git
42367 Roo.bootstrap.PhoneInputData = function() {
42370 "Afghanistan (افغانستان)",
42375 "Albania (Shqipëri)",
42380 "Algeria (الجزائر)",
42405 "Antigua and Barbuda",
42415 "Armenia (Հայաստան)",
42431 "Austria (Österreich)",
42436 "Azerbaijan (Azərbaycan)",
42446 "Bahrain (البحرين)",
42451 "Bangladesh (বাংলাদেশ)",
42461 "Belarus (Беларусь)",
42466 "Belgium (België)",
42496 "Bosnia and Herzegovina (Босна и Херцеговина)",
42511 "British Indian Ocean Territory",
42516 "British Virgin Islands",
42526 "Bulgaria (България)",
42536 "Burundi (Uburundi)",
42541 "Cambodia (កម្ពុជា)",
42546 "Cameroon (Cameroun)",
42555 ["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"]
42558 "Cape Verde (Kabu Verdi)",
42563 "Caribbean Netherlands",
42574 "Central African Republic (République centrafricaine)",
42594 "Christmas Island",
42600 "Cocos (Keeling) Islands",
42611 "Comoros (جزر القمر)",
42616 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42621 "Congo (Republic) (Congo-Brazzaville)",
42641 "Croatia (Hrvatska)",
42662 "Czech Republic (Česká republika)",
42667 "Denmark (Danmark)",
42682 "Dominican Republic (República Dominicana)",
42686 ["809", "829", "849"]
42704 "Equatorial Guinea (Guinea Ecuatorial)",
42724 "Falkland Islands (Islas Malvinas)",
42729 "Faroe Islands (Føroyar)",
42750 "French Guiana (Guyane française)",
42755 "French Polynesia (Polynésie française)",
42770 "Georgia (საქართველო)",
42775 "Germany (Deutschland)",
42795 "Greenland (Kalaallit Nunaat)",
42832 "Guinea-Bissau (Guiné Bissau)",
42857 "Hungary (Magyarország)",
42862 "Iceland (Ísland)",
42882 "Iraq (العراق)",
42898 "Israel (ישראל)",
42925 "Jordan (الأردن)",
42930 "Kazakhstan (Казахстан)",
42951 "Kuwait (الكويت)",
42956 "Kyrgyzstan (Кыргызстан)",
42966 "Latvia (Latvija)",
42971 "Lebanon (لبنان)",
42986 "Libya (ليبيا)",
42996 "Lithuania (Lietuva)",
43011 "Macedonia (FYROM) (Македонија)",
43016 "Madagascar (Madagasikara)",
43046 "Marshall Islands",
43056 "Mauritania (موريتانيا)",
43061 "Mauritius (Moris)",
43082 "Moldova (Republica Moldova)",
43092 "Mongolia (Монгол)",
43097 "Montenegro (Crna Gora)",
43107 "Morocco (المغرب)",
43113 "Mozambique (Moçambique)",
43118 "Myanmar (Burma) (မြန်မာ)",
43123 "Namibia (Namibië)",
43138 "Netherlands (Nederland)",
43143 "New Caledonia (Nouvelle-Calédonie)",
43178 "North Korea (조선 민주주의 인민 공화국)",
43183 "Northern Mariana Islands",
43199 "Pakistan (پاکستان)",
43209 "Palestine (فلسطين)",
43219 "Papua New Guinea",
43261 "Réunion (La Réunion)",
43267 "Romania (România)",
43283 "Saint Barthélemy",
43294 "Saint Kitts and Nevis",
43304 "Saint Martin (Saint-Martin (partie française))",
43310 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43315 "Saint Vincent and the Grenadines",
43330 "São Tomé and Príncipe (São Tomé e Príncipe)",
43335 "Saudi Arabia (المملكة العربية السعودية)",
43340 "Senegal (Sénégal)",
43370 "Slovakia (Slovensko)",
43375 "Slovenia (Slovenija)",
43385 "Somalia (Soomaaliya)",
43395 "South Korea (대한민국)",
43400 "South Sudan (جنوب السودان)",
43410 "Sri Lanka (ශ්රී ලංකාව)",
43415 "Sudan (السودان)",
43425 "Svalbard and Jan Mayen",
43436 "Sweden (Sverige)",
43441 "Switzerland (Schweiz)",
43446 "Syria (سوريا)",
43491 "Trinidad and Tobago",
43496 "Tunisia (تونس)",
43501 "Turkey (Türkiye)",
43511 "Turks and Caicos Islands",
43521 "U.S. Virgin Islands",
43531 "Ukraine (Україна)",
43536 "United Arab Emirates (الإمارات العربية المتحدة)",
43558 "Uzbekistan (Oʻzbekiston)",
43568 "Vatican City (Città del Vaticano)",
43579 "Vietnam (Việt Nam)",
43584 "Wallis and Futuna (Wallis-et-Futuna)",
43589 "Western Sahara (الصحراء الغربية)",
43595 "Yemen (اليمن)",
43619 * This script refer to:
43620 * Title: International Telephone Input
43621 * Author: Jack O'Connor
43622 * Code version: v12.1.12
43623 * Availability: https://github.com/jackocnr/intl-tel-input.git
43627 * @class Roo.bootstrap.PhoneInput
43628 * @extends Roo.bootstrap.TriggerField
43629 * An input with International dial-code selection
43631 * @cfg {String} defaultDialCode default '+852'
43632 * @cfg {Array} preferedCountries default []
43635 * Create a new PhoneInput.
43636 * @param {Object} config Configuration options
43639 Roo.bootstrap.PhoneInput = function(config) {
43640 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43643 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43645 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43647 listWidth: undefined,
43649 selectedClass: 'active',
43651 invalidClass : "has-warning",
43653 validClass: 'has-success',
43655 allowed: '0123456789',
43660 * @cfg {String} defaultDialCode The default dial code when initializing the input
43662 defaultDialCode: '+852',
43665 * @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
43667 preferedCountries: false,
43669 getAutoCreate : function()
43671 var data = Roo.bootstrap.PhoneInputData();
43672 var align = this.labelAlign || this.parentLabelAlign();
43675 this.allCountries = [];
43676 this.dialCodeMapping = [];
43678 for (var i = 0; i < data.length; i++) {
43680 this.allCountries[i] = {
43684 priority: c[3] || 0,
43685 areaCodes: c[4] || null
43687 this.dialCodeMapping[c[2]] = {
43690 priority: c[3] || 0,
43691 areaCodes: c[4] || null
43703 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43704 maxlength: this.max_length,
43705 cls : 'form-control tel-input',
43706 autocomplete: 'new-password'
43709 var hiddenInput = {
43712 cls: 'hidden-tel-input'
43716 hiddenInput.name = this.name;
43719 if (this.disabled) {
43720 input.disabled = true;
43723 var flag_container = {
43740 cls: this.hasFeedback ? 'has-feedback' : '',
43746 cls: 'dial-code-holder',
43753 cls: 'roo-select2-container input-group',
43760 if (this.fieldLabel.length) {
43763 tooltip: 'This field is required'
43769 cls: 'control-label',
43775 html: this.fieldLabel
43778 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43784 if(this.indicatorpos == 'right') {
43785 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43792 if(align == 'left') {
43800 if(this.labelWidth > 12){
43801 label.style = "width: " + this.labelWidth + 'px';
43803 if(this.labelWidth < 13 && this.labelmd == 0){
43804 this.labelmd = this.labelWidth;
43806 if(this.labellg > 0){
43807 label.cls += ' col-lg-' + this.labellg;
43808 input.cls += ' col-lg-' + (12 - this.labellg);
43810 if(this.labelmd > 0){
43811 label.cls += ' col-md-' + this.labelmd;
43812 container.cls += ' col-md-' + (12 - this.labelmd);
43814 if(this.labelsm > 0){
43815 label.cls += ' col-sm-' + this.labelsm;
43816 container.cls += ' col-sm-' + (12 - this.labelsm);
43818 if(this.labelxs > 0){
43819 label.cls += ' col-xs-' + this.labelxs;
43820 container.cls += ' col-xs-' + (12 - this.labelxs);
43830 var settings = this;
43832 ['xs','sm','md','lg'].map(function(size){
43833 if (settings[size]) {
43834 cfg.cls += ' col-' + size + '-' + settings[size];
43838 this.store = new Roo.data.Store({
43839 proxy : new Roo.data.MemoryProxy({}),
43840 reader : new Roo.data.JsonReader({
43851 'name' : 'dialCode',
43855 'name' : 'priority',
43859 'name' : 'areaCodes',
43866 if(!this.preferedCountries) {
43867 this.preferedCountries = [
43874 var p = this.preferedCountries.reverse();
43877 for (var i = 0; i < p.length; i++) {
43878 for (var j = 0; j < this.allCountries.length; j++) {
43879 if(this.allCountries[j].iso2 == p[i]) {
43880 var t = this.allCountries[j];
43881 this.allCountries.splice(j,1);
43882 this.allCountries.unshift(t);
43888 this.store.proxy.data = {
43890 data: this.allCountries
43896 initEvents : function()
43899 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43901 this.indicator = this.indicatorEl();
43902 this.flag = this.flagEl();
43903 this.dialCodeHolder = this.dialCodeHolderEl();
43905 this.trigger = this.el.select('div.flag-box',true).first();
43906 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43911 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43912 _this.list.setWidth(lw);
43915 this.list.on('mouseover', this.onViewOver, this);
43916 this.list.on('mousemove', this.onViewMove, this);
43917 this.inputEl().on("keyup", this.onKeyUp, this);
43918 this.inputEl().on("keypress", this.onKeyPress, this);
43920 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43922 this.view = new Roo.View(this.list, this.tpl, {
43923 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43926 this.view.on('click', this.onViewClick, this);
43927 this.setValue(this.defaultDialCode);
43930 onTriggerClick : function(e)
43932 Roo.log('trigger click');
43937 if(this.isExpanded()){
43939 this.hasFocus = false;
43941 this.store.load({});
43942 this.hasFocus = true;
43947 isExpanded : function()
43949 return this.list.isVisible();
43952 collapse : function()
43954 if(!this.isExpanded()){
43958 Roo.get(document).un('mousedown', this.collapseIf, this);
43959 Roo.get(document).un('mousewheel', this.collapseIf, this);
43960 this.fireEvent('collapse', this);
43964 expand : function()
43968 if(this.isExpanded() || !this.hasFocus){
43972 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43973 this.list.setWidth(lw);
43976 this.restrictHeight();
43978 Roo.get(document).on('mousedown', this.collapseIf, this);
43979 Roo.get(document).on('mousewheel', this.collapseIf, this);
43981 this.fireEvent('expand', this);
43984 restrictHeight : function()
43986 this.list.alignTo(this.inputEl(), this.listAlign);
43987 this.list.alignTo(this.inputEl(), this.listAlign);
43990 onViewOver : function(e, t)
43992 if(this.inKeyMode){
43995 var item = this.view.findItemFromChild(t);
43998 var index = this.view.indexOf(item);
43999 this.select(index, false);
44004 onViewClick : function(view, doFocus, el, e)
44006 var index = this.view.getSelectedIndexes()[0];
44008 var r = this.store.getAt(index);
44011 this.onSelect(r, index);
44013 if(doFocus !== false && !this.blockFocus){
44014 this.inputEl().focus();
44018 onViewMove : function(e, t)
44020 this.inKeyMode = false;
44023 select : function(index, scrollIntoView)
44025 this.selectedIndex = index;
44026 this.view.select(index);
44027 if(scrollIntoView !== false){
44028 var el = this.view.getNode(index);
44030 this.list.scrollChildIntoView(el, false);
44035 createList : function()
44037 this.list = Roo.get(document.body).createChild({
44039 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44040 style: 'display:none'
44043 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44046 collapseIf : function(e)
44048 var in_combo = e.within(this.el);
44049 var in_list = e.within(this.list);
44050 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44052 if (in_combo || in_list || is_list) {
44058 onSelect : function(record, index)
44060 if(this.fireEvent('beforeselect', this, record, index) !== false){
44062 this.setFlagClass(record.data.iso2);
44063 this.setDialCode(record.data.dialCode);
44064 this.hasFocus = false;
44066 this.fireEvent('select', this, record, index);
44070 flagEl : function()
44072 var flag = this.el.select('div.flag',true).first();
44079 dialCodeHolderEl : function()
44081 var d = this.el.select('input.dial-code-holder',true).first();
44088 setDialCode : function(v)
44090 this.dialCodeHolder.dom.value = '+'+v;
44093 setFlagClass : function(n)
44095 this.flag.dom.className = 'flag '+n;
44098 getValue : function()
44100 var v = this.inputEl().getValue();
44101 if(this.dialCodeHolder) {
44102 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44107 setValue : function(v)
44109 var d = this.getDialCode(v);
44111 //invalid dial code
44112 if(v.length == 0 || !d || d.length == 0) {
44114 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44115 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44121 this.setFlagClass(this.dialCodeMapping[d].iso2);
44122 this.setDialCode(d);
44123 this.inputEl().dom.value = v.replace('+'+d,'');
44124 this.hiddenEl().dom.value = this.getValue();
44129 getDialCode : function(v)
44133 if (v.length == 0) {
44134 return this.dialCodeHolder.dom.value;
44138 if (v.charAt(0) != "+") {
44141 var numericChars = "";
44142 for (var i = 1; i < v.length; i++) {
44143 var c = v.charAt(i);
44146 if (this.dialCodeMapping[numericChars]) {
44147 dialCode = v.substr(1, i);
44149 if (numericChars.length == 4) {
44159 this.setValue(this.defaultDialCode);
44163 hiddenEl : function()
44165 return this.el.select('input.hidden-tel-input',true).first();
44168 // after setting val
44169 onKeyUp : function(e){
44170 this.setValue(this.getValue());
44173 onKeyPress : function(e){
44174 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44181 * @class Roo.bootstrap.MoneyField
44182 * @extends Roo.bootstrap.ComboBox
44183 * Bootstrap MoneyField class
44186 * Create a new MoneyField.
44187 * @param {Object} config Configuration options
44190 Roo.bootstrap.MoneyField = function(config) {
44192 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44196 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44199 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44201 allowDecimals : true,
44203 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44205 decimalSeparator : ".",
44207 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44209 decimalPrecision : 0,
44211 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44213 allowNegative : true,
44215 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44219 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44221 minValue : Number.NEGATIVE_INFINITY,
44223 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44225 maxValue : Number.MAX_VALUE,
44227 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44229 minText : "The minimum value for this field is {0}",
44231 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44233 maxText : "The maximum value for this field is {0}",
44235 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44236 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44238 nanText : "{0} is not a valid number",
44240 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44244 * @cfg {String} defaults currency of the MoneyField
44245 * value should be in lkey
44247 defaultCurrency : false,
44249 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44251 thousandsDelimiter : false,
44253 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44264 getAutoCreate : function()
44266 var align = this.labelAlign || this.parentLabelAlign();
44278 cls : 'form-control roo-money-amount-input',
44279 autocomplete: 'new-password'
44282 var hiddenInput = {
44286 cls: 'hidden-number-input'
44289 if(this.max_length) {
44290 input.maxlength = this.max_length;
44294 hiddenInput.name = this.name;
44297 if (this.disabled) {
44298 input.disabled = true;
44301 var clg = 12 - this.inputlg;
44302 var cmd = 12 - this.inputmd;
44303 var csm = 12 - this.inputsm;
44304 var cxs = 12 - this.inputxs;
44308 cls : 'row roo-money-field',
44312 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44316 cls: 'roo-select2-container input-group',
44320 cls : 'form-control roo-money-currency-input',
44321 autocomplete: 'new-password',
44323 name : this.currencyName
44327 cls : 'input-group-addon',
44341 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44345 cls: this.hasFeedback ? 'has-feedback' : '',
44356 if (this.fieldLabel.length) {
44359 tooltip: 'This field is required'
44365 cls: 'control-label',
44371 html: this.fieldLabel
44374 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44380 if(this.indicatorpos == 'right') {
44381 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44388 if(align == 'left') {
44396 if(this.labelWidth > 12){
44397 label.style = "width: " + this.labelWidth + 'px';
44399 if(this.labelWidth < 13 && this.labelmd == 0){
44400 this.labelmd = this.labelWidth;
44402 if(this.labellg > 0){
44403 label.cls += ' col-lg-' + this.labellg;
44404 input.cls += ' col-lg-' + (12 - this.labellg);
44406 if(this.labelmd > 0){
44407 label.cls += ' col-md-' + this.labelmd;
44408 container.cls += ' col-md-' + (12 - this.labelmd);
44410 if(this.labelsm > 0){
44411 label.cls += ' col-sm-' + this.labelsm;
44412 container.cls += ' col-sm-' + (12 - this.labelsm);
44414 if(this.labelxs > 0){
44415 label.cls += ' col-xs-' + this.labelxs;
44416 container.cls += ' col-xs-' + (12 - this.labelxs);
44427 var settings = this;
44429 ['xs','sm','md','lg'].map(function(size){
44430 if (settings[size]) {
44431 cfg.cls += ' col-' + size + '-' + settings[size];
44438 initEvents : function()
44440 this.indicator = this.indicatorEl();
44442 this.initCurrencyEvent();
44444 this.initNumberEvent();
44447 initCurrencyEvent : function()
44450 throw "can not find store for combo";
44453 this.store = Roo.factory(this.store, Roo.data);
44454 this.store.parent = this;
44458 this.triggerEl = this.el.select('.input-group-addon', true).first();
44460 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44465 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44466 _this.list.setWidth(lw);
44469 this.list.on('mouseover', this.onViewOver, this);
44470 this.list.on('mousemove', this.onViewMove, this);
44471 this.list.on('scroll', this.onViewScroll, this);
44474 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44477 this.view = new Roo.View(this.list, this.tpl, {
44478 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44481 this.view.on('click', this.onViewClick, this);
44483 this.store.on('beforeload', this.onBeforeLoad, this);
44484 this.store.on('load', this.onLoad, this);
44485 this.store.on('loadexception', this.onLoadException, this);
44487 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44488 "up" : function(e){
44489 this.inKeyMode = true;
44493 "down" : function(e){
44494 if(!this.isExpanded()){
44495 this.onTriggerClick();
44497 this.inKeyMode = true;
44502 "enter" : function(e){
44505 if(this.fireEvent("specialkey", this, e)){
44506 this.onViewClick(false);
44512 "esc" : function(e){
44516 "tab" : function(e){
44519 if(this.fireEvent("specialkey", this, e)){
44520 this.onViewClick(false);
44528 doRelay : function(foo, bar, hname){
44529 if(hname == 'down' || this.scope.isExpanded()){
44530 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44538 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44542 initNumberEvent : function(e)
44544 this.inputEl().on("keydown" , this.fireKey, this);
44545 this.inputEl().on("focus", this.onFocus, this);
44546 this.inputEl().on("blur", this.onBlur, this);
44548 this.inputEl().relayEvent('keyup', this);
44550 if(this.indicator){
44551 this.indicator.addClass('invisible');
44554 this.originalValue = this.getValue();
44556 if(this.validationEvent == 'keyup'){
44557 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44558 this.inputEl().on('keyup', this.filterValidation, this);
44560 else if(this.validationEvent !== false){
44561 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44564 if(this.selectOnFocus){
44565 this.on("focus", this.preFocus, this);
44568 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44569 this.inputEl().on("keypress", this.filterKeys, this);
44571 this.inputEl().relayEvent('keypress', this);
44574 var allowed = "0123456789";
44576 if(this.allowDecimals){
44577 allowed += this.decimalSeparator;
44580 if(this.allowNegative){
44584 if(this.thousandsDelimiter) {
44588 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44590 var keyPress = function(e){
44592 var k = e.getKey();
44594 var c = e.getCharCode();
44597 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44598 allowed.indexOf(String.fromCharCode(c)) === -1
44604 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44608 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44613 this.inputEl().on("keypress", keyPress, this);
44617 onTriggerClick : function(e)
44624 this.loadNext = false;
44626 if(this.isExpanded()){
44631 this.hasFocus = true;
44633 if(this.triggerAction == 'all') {
44634 this.doQuery(this.allQuery, true);
44638 this.doQuery(this.getRawValue());
44641 getCurrency : function()
44643 var v = this.currencyEl().getValue();
44648 restrictHeight : function()
44650 this.list.alignTo(this.currencyEl(), this.listAlign);
44651 this.list.alignTo(this.currencyEl(), this.listAlign);
44654 onViewClick : function(view, doFocus, el, e)
44656 var index = this.view.getSelectedIndexes()[0];
44658 var r = this.store.getAt(index);
44661 this.onSelect(r, index);
44665 onSelect : function(record, index){
44667 if(this.fireEvent('beforeselect', this, record, index) !== false){
44669 this.setFromCurrencyData(index > -1 ? record.data : false);
44673 this.fireEvent('select', this, record, index);
44677 setFromCurrencyData : function(o)
44681 this.lastCurrency = o;
44683 if (this.currencyField) {
44684 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44686 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44689 this.lastSelectionText = currency;
44691 //setting default currency
44692 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44693 this.setCurrency(this.defaultCurrency);
44697 this.setCurrency(currency);
44700 setFromData : function(o)
44704 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44706 this.setFromCurrencyData(c);
44711 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44713 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44716 this.setValue(value);
44720 setCurrency : function(v)
44722 this.currencyValue = v;
44725 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44730 setValue : function(v)
44732 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44738 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44740 this.inputEl().dom.value = (v == '') ? '' :
44741 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44743 if(!this.allowZero && v === '0') {
44744 this.hiddenEl().dom.value = '';
44745 this.inputEl().dom.value = '';
44752 getRawValue : function()
44754 var v = this.inputEl().getValue();
44759 getValue : function()
44761 return this.fixPrecision(this.parseValue(this.getRawValue()));
44764 parseValue : function(value)
44766 if(this.thousandsDelimiter) {
44768 r = new RegExp(",", "g");
44769 value = value.replace(r, "");
44772 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44773 return isNaN(value) ? '' : value;
44777 fixPrecision : function(value)
44779 if(this.thousandsDelimiter) {
44781 r = new RegExp(",", "g");
44782 value = value.replace(r, "");
44785 var nan = isNaN(value);
44787 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44788 return nan ? '' : value;
44790 return parseFloat(value).toFixed(this.decimalPrecision);
44793 decimalPrecisionFcn : function(v)
44795 return Math.floor(v);
44798 validateValue : function(value)
44800 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44804 var num = this.parseValue(value);
44807 this.markInvalid(String.format(this.nanText, value));
44811 if(num < this.minValue){
44812 this.markInvalid(String.format(this.minText, this.minValue));
44816 if(num > this.maxValue){
44817 this.markInvalid(String.format(this.maxText, this.maxValue));
44824 validate : function()
44826 if(this.disabled || this.allowBlank){
44831 var currency = this.getCurrency();
44833 if(this.validateValue(this.getRawValue()) && currency.length){
44838 this.markInvalid();
44842 getName: function()
44847 beforeBlur : function()
44853 var v = this.parseValue(this.getRawValue());
44860 onBlur : function()
44864 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44865 //this.el.removeClass(this.focusClass);
44868 this.hasFocus = false;
44870 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44874 var v = this.getValue();
44876 if(String(v) !== String(this.startValue)){
44877 this.fireEvent('change', this, v, this.startValue);
44880 this.fireEvent("blur", this);
44883 inputEl : function()
44885 return this.el.select('.roo-money-amount-input', true).first();
44888 currencyEl : function()
44890 return this.el.select('.roo-money-currency-input', true).first();
44893 hiddenEl : function()
44895 return this.el.select('input.hidden-number-input',true).first();
44899 * @class Roo.bootstrap.BezierSignature
44900 * @extends Roo.bootstrap.Component
44901 * Bootstrap BezierSignature class
44902 * This script refer to:
44903 * Title: Signature Pad
44905 * Availability: https://github.com/szimek/signature_pad
44908 * Create a new BezierSignature
44909 * @param {Object} config The config object
44912 Roo.bootstrap.BezierSignature = function(config){
44913 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44919 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44926 mouse_btn_down: true,
44929 * @cfg {int} canvas height
44931 canvas_height: '200px',
44934 * @cfg {float|function} Radius of a single dot.
44939 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44944 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44949 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44954 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44959 * @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.
44961 bg_color: 'rgba(0, 0, 0, 0)',
44964 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44966 dot_color: 'black',
44969 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44971 velocity_filter_weight: 0.7,
44974 * @cfg {function} Callback when stroke begin.
44979 * @cfg {function} Callback when stroke end.
44983 getAutoCreate : function()
44985 var cls = 'roo-signature column';
44988 cls += ' ' + this.cls;
44998 for(var i = 0; i < col_sizes.length; i++) {
44999 if(this[col_sizes[i]]) {
45000 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45010 cls: 'roo-signature-body',
45014 cls: 'roo-signature-body-canvas',
45015 height: this.canvas_height,
45016 width: this.canvas_width
45023 style: 'display: none'
45031 initEvents: function()
45033 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45035 var canvas = this.canvasEl();
45037 // mouse && touch event swapping...
45038 canvas.dom.style.touchAction = 'none';
45039 canvas.dom.style.msTouchAction = 'none';
45041 this.mouse_btn_down = false;
45042 canvas.on('mousedown', this._handleMouseDown, this);
45043 canvas.on('mousemove', this._handleMouseMove, this);
45044 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45046 if (window.PointerEvent) {
45047 canvas.on('pointerdown', this._handleMouseDown, this);
45048 canvas.on('pointermove', this._handleMouseMove, this);
45049 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45052 if ('ontouchstart' in window) {
45053 canvas.on('touchstart', this._handleTouchStart, this);
45054 canvas.on('touchmove', this._handleTouchMove, this);
45055 canvas.on('touchend', this._handleTouchEnd, this);
45058 Roo.EventManager.onWindowResize(this.resize, this, true);
45060 // file input event
45061 this.fileEl().on('change', this.uploadImage, this);
45068 resize: function(){
45070 var canvas = this.canvasEl().dom;
45071 var ctx = this.canvasElCtx();
45072 var img_data = false;
45074 if(canvas.width > 0) {
45075 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45077 // setting canvas width will clean img data
45080 var style = window.getComputedStyle ?
45081 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45083 var padding_left = parseInt(style.paddingLeft) || 0;
45084 var padding_right = parseInt(style.paddingRight) || 0;
45086 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45089 ctx.putImageData(img_data, 0, 0);
45093 _handleMouseDown: function(e)
45095 if (e.browserEvent.which === 1) {
45096 this.mouse_btn_down = true;
45097 this.strokeBegin(e);
45101 _handleMouseMove: function (e)
45103 if (this.mouse_btn_down) {
45104 this.strokeMoveUpdate(e);
45108 _handleMouseUp: function (e)
45110 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45111 this.mouse_btn_down = false;
45116 _handleTouchStart: function (e) {
45118 e.preventDefault();
45119 if (e.browserEvent.targetTouches.length === 1) {
45120 // var touch = e.browserEvent.changedTouches[0];
45121 // this.strokeBegin(touch);
45123 this.strokeBegin(e); // assume e catching the correct xy...
45127 _handleTouchMove: function (e) {
45128 e.preventDefault();
45129 // var touch = event.targetTouches[0];
45130 // _this._strokeMoveUpdate(touch);
45131 this.strokeMoveUpdate(e);
45134 _handleTouchEnd: function (e) {
45135 var wasCanvasTouched = e.target === this.canvasEl().dom;
45136 if (wasCanvasTouched) {
45137 e.preventDefault();
45138 // var touch = event.changedTouches[0];
45139 // _this._strokeEnd(touch);
45144 reset: function () {
45145 this._lastPoints = [];
45146 this._lastVelocity = 0;
45147 this._lastWidth = (this.min_width + this.max_width) / 2;
45148 this.canvasElCtx().fillStyle = this.dot_color;
45151 strokeMoveUpdate: function(e)
45153 this.strokeUpdate(e);
45155 if (this.throttle) {
45156 this.throttleStroke(this.strokeUpdate, this.throttle);
45159 this.strokeUpdate(e);
45163 strokeBegin: function(e)
45165 var newPointGroup = {
45166 color: this.dot_color,
45170 if (typeof this.onBegin === 'function') {
45174 this.curve_data.push(newPointGroup);
45176 this.strokeUpdate(e);
45179 strokeUpdate: function(e)
45181 var rect = this.canvasEl().dom.getBoundingClientRect();
45182 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45183 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45184 var lastPoints = lastPointGroup.points;
45185 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45186 var isLastPointTooClose = lastPoint
45187 ? point.distanceTo(lastPoint) <= this.min_distance
45189 var color = lastPointGroup.color;
45190 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45191 var curve = this.addPoint(point);
45193 this.drawDot({color: color, point: point});
45196 this.drawCurve({color: color, curve: curve});
45206 strokeEnd: function(e)
45208 this.strokeUpdate(e);
45209 if (typeof this.onEnd === 'function') {
45214 addPoint: function (point) {
45215 var _lastPoints = this._lastPoints;
45216 _lastPoints.push(point);
45217 if (_lastPoints.length > 2) {
45218 if (_lastPoints.length === 3) {
45219 _lastPoints.unshift(_lastPoints[0]);
45221 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45222 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45223 _lastPoints.shift();
45229 calculateCurveWidths: function (startPoint, endPoint) {
45230 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45231 (1 - this.velocity_filter_weight) * this._lastVelocity;
45233 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45236 start: this._lastWidth
45239 this._lastVelocity = velocity;
45240 this._lastWidth = newWidth;
45244 drawDot: function (_a) {
45245 var color = _a.color, point = _a.point;
45246 var ctx = this.canvasElCtx();
45247 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45249 this.drawCurveSegment(point.x, point.y, width);
45251 ctx.fillStyle = color;
45255 drawCurve: function (_a) {
45256 var color = _a.color, curve = _a.curve;
45257 var ctx = this.canvasElCtx();
45258 var widthDelta = curve.endWidth - curve.startWidth;
45259 var drawSteps = Math.floor(curve.length()) * 2;
45261 ctx.fillStyle = color;
45262 for (var i = 0; i < drawSteps; i += 1) {
45263 var t = i / drawSteps;
45269 var x = uuu * curve.startPoint.x;
45270 x += 3 * uu * t * curve.control1.x;
45271 x += 3 * u * tt * curve.control2.x;
45272 x += ttt * curve.endPoint.x;
45273 var y = uuu * curve.startPoint.y;
45274 y += 3 * uu * t * curve.control1.y;
45275 y += 3 * u * tt * curve.control2.y;
45276 y += ttt * curve.endPoint.y;
45277 var width = curve.startWidth + ttt * widthDelta;
45278 this.drawCurveSegment(x, y, width);
45284 drawCurveSegment: function (x, y, width) {
45285 var ctx = this.canvasElCtx();
45287 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45288 this.is_empty = false;
45293 var ctx = this.canvasElCtx();
45294 var canvas = this.canvasEl().dom;
45295 ctx.fillStyle = this.bg_color;
45296 ctx.clearRect(0, 0, canvas.width, canvas.height);
45297 ctx.fillRect(0, 0, canvas.width, canvas.height);
45298 this.curve_data = [];
45300 this.is_empty = true;
45305 return this.el.select('input',true).first();
45308 canvasEl: function()
45310 return this.el.select('canvas',true).first();
45313 canvasElCtx: function()
45315 return this.el.select('canvas',true).first().dom.getContext('2d');
45318 getImage: function(type)
45320 if(this.is_empty) {
45325 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45328 drawFromImage: function(img_src)
45330 var img = new Image();
45332 img.onload = function(){
45333 this.canvasElCtx().drawImage(img, 0, 0);
45338 this.is_empty = false;
45341 selectImage: function()
45343 this.fileEl().dom.click();
45346 uploadImage: function(e)
45348 var reader = new FileReader();
45350 reader.onload = function(e){
45351 var img = new Image();
45352 img.onload = function(){
45354 this.canvasElCtx().drawImage(img, 0, 0);
45356 img.src = e.target.result;
45359 reader.readAsDataURL(e.target.files[0]);
45362 // Bezier Point Constructor
45363 Point: (function () {
45364 function Point(x, y, time) {
45367 this.time = time || Date.now();
45369 Point.prototype.distanceTo = function (start) {
45370 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45372 Point.prototype.equals = function (other) {
45373 return this.x === other.x && this.y === other.y && this.time === other.time;
45375 Point.prototype.velocityFrom = function (start) {
45376 return this.time !== start.time
45377 ? this.distanceTo(start) / (this.time - start.time)
45384 // Bezier Constructor
45385 Bezier: (function () {
45386 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45387 this.startPoint = startPoint;
45388 this.control2 = control2;
45389 this.control1 = control1;
45390 this.endPoint = endPoint;
45391 this.startWidth = startWidth;
45392 this.endWidth = endWidth;
45394 Bezier.fromPoints = function (points, widths, scope) {
45395 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45396 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45397 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45399 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45400 var dx1 = s1.x - s2.x;
45401 var dy1 = s1.y - s2.y;
45402 var dx2 = s2.x - s3.x;
45403 var dy2 = s2.y - s3.y;
45404 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45405 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45406 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45407 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45408 var dxm = m1.x - m2.x;
45409 var dym = m1.y - m2.y;
45410 var k = l2 / (l1 + l2);
45411 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45412 var tx = s2.x - cm.x;
45413 var ty = s2.y - cm.y;
45415 c1: new scope.Point(m1.x + tx, m1.y + ty),
45416 c2: new scope.Point(m2.x + tx, m2.y + ty)
45419 Bezier.prototype.length = function () {
45424 for (var i = 0; i <= steps; i += 1) {
45426 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45427 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45429 var xdiff = cx - px;
45430 var ydiff = cy - py;
45431 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45438 Bezier.prototype.point = function (t, start, c1, c2, end) {
45439 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45440 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45441 + (3.0 * c2 * (1.0 - t) * t * t)
45442 + (end * t * t * t);
45447 throttleStroke: function(fn, wait) {
45448 if (wait === void 0) { wait = 250; }
45450 var timeout = null;
45454 var later = function () {
45455 previous = Date.now();
45457 result = fn.apply(storedContext, storedArgs);
45459 storedContext = null;
45463 return function wrapper() {
45465 for (var _i = 0; _i < arguments.length; _i++) {
45466 args[_i] = arguments[_i];
45468 var now = Date.now();
45469 var remaining = wait - (now - previous);
45470 storedContext = this;
45472 if (remaining <= 0 || remaining > wait) {
45474 clearTimeout(timeout);
45478 result = fn.apply(storedContext, storedArgs);
45480 storedContext = null;
45484 else if (!timeout) {
45485 timeout = window.setTimeout(later, remaining);