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
1598 * Bootstrap Container class
1599 * @cfg {Boolean} jumbotron is it a jumbotron element
1600 * @cfg {String} html content of element
1601 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1602 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1603 * @cfg {String} header content of header (for panel)
1604 * @cfg {String} footer content of footer (for panel)
1605 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1606 * @cfg {String} tag (header|aside|section) type of HTML tag.
1607 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1608 * @cfg {String} fa font awesome icon
1609 * @cfg {String} icon (info-sign|check|...) glyphicon name
1610 * @cfg {Boolean} hidden (true|false) hide the element
1611 * @cfg {Boolean} expandable (true|false) default false
1612 * @cfg {Boolean} expanded (true|false) default true
1613 * @cfg {String} rheader contet on the right of header
1614 * @cfg {Boolean} clickable (true|false) default false
1618 * Create a new Container
1619 * @param {Object} config The config object
1622 Roo.bootstrap.Container = function(config){
1623 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1629 * After the panel has been expand
1631 * @param {Roo.bootstrap.Container} this
1636 * After the panel has been collapsed
1638 * @param {Roo.bootstrap.Container} this
1643 * When a element is chick
1644 * @param {Roo.bootstrap.Container} this
1645 * @param {Roo.EventObject} e
1651 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1669 getChildContainer : function() {
1675 if (this.panel.length) {
1676 return this.el.select('.panel-body',true).first();
1683 getAutoCreate : function(){
1686 tag : this.tag || 'div',
1690 if (this.jumbotron) {
1691 cfg.cls = 'jumbotron';
1696 // - this is applied by the parent..
1698 // cfg.cls = this.cls + '';
1701 if (this.sticky.length) {
1703 var bd = Roo.get(document.body);
1704 if (!bd.hasClass('bootstrap-sticky')) {
1705 bd.addClass('bootstrap-sticky');
1706 Roo.select('html',true).setStyle('height', '100%');
1709 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1713 if (this.well.length) {
1714 switch (this.well) {
1717 cfg.cls +=' well well-' +this.well;
1726 cfg.cls += ' hidden';
1730 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1731 cfg.cls +=' alert alert-' + this.alert;
1736 if (this.panel.length) {
1737 cfg.cls += ' panel panel-' + this.panel;
1739 if (this.header.length) {
1743 if(this.expandable){
1745 cfg.cls = cfg.cls + ' expandable';
1749 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1757 cls : 'panel-title',
1758 html : (this.expandable ? ' ' : '') + this.header
1762 cls: 'panel-header-right',
1768 cls : 'panel-heading',
1769 style : this.expandable ? 'cursor: pointer' : '',
1777 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1782 if (this.footer.length) {
1784 cls : 'panel-footer',
1793 body.html = this.html || cfg.html;
1794 // prefix with the icons..
1796 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1799 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1804 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1805 cfg.cls = 'container';
1811 initEvents: function()
1813 if(this.expandable){
1814 var headerEl = this.headerEl();
1817 headerEl.on('click', this.onToggleClick, this);
1822 this.el.on('click', this.onClick, this);
1827 onToggleClick : function()
1829 var headerEl = this.headerEl();
1845 if(this.fireEvent('expand', this)) {
1847 this.expanded = true;
1849 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1851 this.el.select('.panel-body',true).first().removeClass('hide');
1853 var toggleEl = this.toggleEl();
1859 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1864 collapse : function()
1866 if(this.fireEvent('collapse', this)) {
1868 this.expanded = false;
1870 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1871 this.el.select('.panel-body',true).first().addClass('hide');
1873 var toggleEl = this.toggleEl();
1879 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1883 toggleEl : function()
1885 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1889 return this.el.select('.panel-heading .fa',true).first();
1892 headerEl : function()
1894 if(!this.el || !this.panel.length || !this.header.length){
1898 return this.el.select('.panel-heading',true).first()
1903 if(!this.el || !this.panel.length){
1907 return this.el.select('.panel-body',true).first()
1910 titleEl : function()
1912 if(!this.el || !this.panel.length || !this.header.length){
1916 return this.el.select('.panel-title',true).first();
1919 setTitle : function(v)
1921 var titleEl = this.titleEl();
1927 titleEl.dom.innerHTML = v;
1930 getTitle : function()
1933 var titleEl = this.titleEl();
1939 return titleEl.dom.innerHTML;
1942 setRightTitle : function(v)
1944 var t = this.el.select('.panel-header-right',true).first();
1950 t.dom.innerHTML = v;
1953 onClick : function(e)
1957 this.fireEvent('click', this, e);
1964 * This is BS4's Card element.. - similar to our containers probably..
1968 * @class Roo.bootstrap.Card
1969 * @extends Roo.bootstrap.Component
1970 * Bootstrap Card class
1973 * possible... may not be implemented..
1974 * @cfg {String} header_image src url of image.
1975 * @cfg {String|Object} header
1976 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1977 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1979 * @cfg {String} title
1980 * @cfg {String} subtitle
1981 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1982 * @cfg {String} footer
1984 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1986 * @cfg {String} margin (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1990 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1991 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1992 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1994 * @cfg {String} padding (0|1|2|3|4|5)
1995 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1996 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1997 * @cfg {String} padding_left (0|1|2|3|4|5)
1998 * @cfg {String} padding_right (0|1|2|3|4|5)
1999 * @cfg {String} padding_x (0|1|2|3|4|5)
2000 * @cfg {String} padding_y (0|1|2|3|4|5)
2002 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2006 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2008 * @config {Boolean} dragable if this card can be dragged.
2009 * @config {String} drag_group group for drag
2010 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2011 * @config {String} drop_group group for drag
2013 * @config {Boolean} collapsable can the body be collapsed.
2014 * @config {Boolean} collapsed is the body collapsed when rendered...
2015 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2016 * @config {Boolean} rotated is the body rotated when rendered...
2019 * Create a new Container
2020 * @param {Object} config The config object
2023 Roo.bootstrap.Card = function(config){
2024 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2030 * When a element a card is dropped
2031 * @param {Roo.bootstrap.Card} this
2034 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2035 * @param {String} position 'above' or 'below'
2036 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2042 * When a element a card is rotate
2043 * @param {Roo.bootstrap.Card} this
2044 * @param {Roo.Element} n the node being dropped?
2045 * @param {Boolean} rotate status
2050 * When a card element is dragged over ready to drop (return false to block dropable)
2051 * @param {Roo.bootstrap.Card} this
2052 * @param {Object} data from dragdrop
2060 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2065 margin: '', /// may be better in component?
2095 collapsable : false,
2104 childContainer : false,
2105 dropEl : false, /// the dom placeholde element that indicates drop location.
2106 containerEl: false, // body container
2107 bodyEl: false, // card-body
2108 headerContainerEl : false, //
2110 header_imageEl : false,
2113 layoutCls : function()
2117 Roo.log(this.margin_bottom.length);
2118 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2119 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2121 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2124 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2125 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2129 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2130 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2131 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2135 // more generic support?
2143 // Roo.log("Call onRender: " + this.xtype);
2144 /* We are looking at something like this.
2146 <img src="..." class="card-img-top" alt="...">
2147 <div class="card-body">
2148 <h5 class="card-title">Card title</h5>
2149 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2151 >> this bit is really the body...
2152 <div> << we will ad dthis in hopefully it will not break shit.
2154 ** card text does not actually have any styling...
2156 <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>
2159 <a href="#" class="card-link">Card link</a>
2162 <div class="card-footer">
2163 <small class="text-muted">Last updated 3 mins ago</small>
2167 getAutoCreate : function(){
2175 if (this.weight.length && this.weight != 'light') {
2176 cfg.cls += ' text-white';
2178 cfg.cls += ' text-dark'; // need as it's nested..
2180 if (this.weight.length) {
2181 cfg.cls += ' bg-' + this.weight;
2184 cfg.cls += ' ' + this.layoutCls();
2187 var hdr_ctr = false;
2188 if (this.header.length) {
2190 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2191 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2199 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2205 if (this.collapsable) {
2208 cls : 'd-block user-select-none',
2212 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2217 hdr.cn.push(hdr_ctr);
2222 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2227 if (this.header_image.length) {
2230 cls : 'card-img-top',
2231 src: this.header_image // escape?
2236 cls : 'card-img-top d-none'
2242 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2246 if (this.collapsable || this.rotateable) {
2249 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2256 if (this.title.length) {
2260 src: this.title // escape?
2264 if (this.subtitle.length) {
2268 src: this.subtitle // escape?
2274 cls : 'roo-card-body-ctr'
2277 if (this.html.length) {
2283 // fixme ? handle objects?
2285 if (this.footer.length) {
2288 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2293 cfg.cn.push({cls : 'card-footer d-none'});
2302 getCardHeader : function()
2304 var ret = this.el.select('.card-header',true).first();
2305 if (ret.hasClass('d-none')) {
2306 ret.removeClass('d-none');
2311 getCardFooter : function()
2313 var ret = this.el.select('.card-footer',true).first();
2314 if (ret.hasClass('d-none')) {
2315 ret.removeClass('d-none');
2320 getCardImageTop : function()
2322 var ret = this.header_imageEl;
2323 if (ret.hasClass('d-none')) {
2324 ret.removeClass('d-none');
2330 getChildContainer : function()
2336 return this.el.select('.roo-card-body-ctr',true).first();
2339 initEvents: function()
2341 this.bodyEl = this.el.select('.card-body',true).first();
2342 this.containerEl = this.getChildContainer();
2344 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2345 containerScroll: true,
2346 ddGroup: this.drag_group || 'default_card_drag_group'
2348 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2350 if (this.dropable) {
2351 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2352 containerScroll: true,
2353 ddGroup: this.drop_group || 'default_card_drag_group'
2355 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2356 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2357 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2358 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2359 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2362 if (this.collapsable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2365 if (this.rotateable) {
2366 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2368 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2370 this.footerEl = this.el.select('.card-footer',true).first();
2371 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2372 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2373 this.headerEl = this.el.select('.card-header',true).first();
2376 this.el.addClass('roo-card-rotated');
2377 this.fireEvent('rotate', this, true);
2379 this.header_imageEl = this.el.select('.card-img-top',true).first();
2380 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2383 getDragData : function(e)
2385 var target = this.getEl();
2387 //this.handleSelection(e);
2392 nodes: this.getEl(),
2397 dragData.ddel = target.dom ; // the div element
2398 Roo.log(target.getWidth( ));
2399 dragData.ddel.style.width = target.getWidth() + 'px';
2406 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2407 * whole Element becomes the target, and this causes the drop gesture to append.
2409 * Returns an object:
2412 position : 'below' or 'above'
2413 card : relateive to card OBJECT (or true for no cards listed)
2414 items_n : relative to nth item in list
2415 card_n : relative to nth card in list
2420 getTargetFromEvent : function(e, dragged_card_el)
2422 var target = e.getTarget();
2423 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2424 target = target.parentNode;
2435 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2436 // see if target is one of the 'cards'...
2439 //Roo.log(this.items.length);
2442 var last_card_n = 0;
2444 for (var i = 0;i< this.items.length;i++) {
2446 if (!this.items[i].el.hasClass('card')) {
2449 pos = this.getDropPoint(e, this.items[i].el.dom);
2451 cards_len = ret.cards.length;
2452 //Roo.log(this.items[i].el.dom.id);
2453 ret.cards.push(this.items[i]);
2455 if (ret.card_n < 0 && pos == 'above') {
2456 ret.position = cards_len > 0 ? 'below' : pos;
2457 ret.items_n = i > 0 ? i - 1 : 0;
2458 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2459 ret.card = ret.cards[ret.card_n];
2462 if (!ret.cards.length) {
2464 ret.position = 'below';
2468 // could not find a card.. stick it at the end..
2469 if (ret.card_n < 0) {
2470 ret.card_n = last_card_n;
2471 ret.card = ret.cards[last_card_n];
2472 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2473 ret.position = 'below';
2476 if (this.items[ret.items_n].el == dragged_card_el) {
2480 if (ret.position == 'below') {
2481 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2483 if (card_after && card_after.el == dragged_card_el) {
2490 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2492 if (card_before && card_before.el == dragged_card_el) {
2499 onNodeEnter : function(n, dd, e, data){
2502 onNodeOver : function(n, dd, e, data)
2505 var target_info = this.getTargetFromEvent(e,data.source.el);
2506 if (target_info === false) {
2507 this.dropPlaceHolder('hide');
2510 Roo.log(['getTargetFromEvent', target_info ]);
2513 if (this.fireEvent('cardover', this, [ data ]) === false) {
2517 this.dropPlaceHolder('show', target_info,data);
2521 onNodeOut : function(n, dd, e, data){
2522 this.dropPlaceHolder('hide');
2525 onNodeDrop : function(n, dd, e, data)
2528 // call drop - return false if
2530 // this could actually fail - if the Network drops..
2531 // we will ignore this at present..- client should probably reload
2532 // the whole set of cards if stuff like that fails.
2535 var info = this.getTargetFromEvent(e,data.source.el);
2536 if (info === false) {
2539 this.dropPlaceHolder('hide');
2543 this.acceptCard(data.source, info.position, info.card, info.items_n);
2547 firstChildCard : function()
2549 for (var i = 0;i< this.items.length;i++) {
2551 if (!this.items[i].el.hasClass('card')) {
2554 return this.items[i];
2556 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2561 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2563 acceptCard : function(move_card, position, next_to_card )
2565 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2569 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2571 move_card.parent().removeCard(move_card);
2574 var dom = move_card.el.dom;
2575 dom.style.width = ''; // clear with - which is set by drag.
2577 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2578 var cardel = next_to_card.el.dom;
2580 if (position == 'above' ) {
2581 cardel.parentNode.insertBefore(dom, cardel);
2582 } else if (cardel.nextSibling) {
2583 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2585 cardel.parentNode.append(dom);
2588 // card container???
2589 this.containerEl.dom.append(dom);
2592 //FIXME HANDLE card = true
2594 // add this to the correct place in items.
2596 // remove Card from items.
2599 if (this.items.length) {
2601 //Roo.log([info.items_n, info.position, this.items.length]);
2602 for (var i =0; i < this.items.length; i++) {
2603 if (i == to_items_n && position == 'above') {
2604 nitems.push(move_card);
2606 nitems.push(this.items[i]);
2607 if (i == to_items_n && position == 'below') {
2608 nitems.push(move_card);
2611 this.items = nitems;
2612 Roo.log(this.items);
2614 this.items.push(move_card);
2617 move_card.parentId = this.id;
2623 removeCard : function(c)
2625 this.items = this.items.filter(function(e) { return e != c });
2628 dom.parentNode.removeChild(dom);
2629 dom.style.width = ''; // clear with - which is set by drag.
2634 /** Decide whether to drop above or below a View node. */
2635 getDropPoint : function(e, n, dd)
2640 if (n == this.containerEl.dom) {
2643 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2644 var c = t + (b - t) / 2;
2645 var y = Roo.lib.Event.getPageY(e);
2652 onToggleCollapse : function(e)
2654 if (this.collapsed) {
2655 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2656 this.collapsableEl.addClass('show');
2657 this.collapsed = false;
2660 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2661 this.collapsableEl.removeClass('show');
2662 this.collapsed = true;
2667 onToggleRotate : function(e)
2669 this.collapsableEl.removeClass('show');
2670 this.footerEl.removeClass('d-none');
2671 this.el.removeClass('roo-card-rotated');
2672 this.el.removeClass('d-none');
2675 this.collapsableEl.addClass('show');
2676 this.rotated = false;
2677 this.fireEvent('rotate', this, this.rotated);
2680 this.el.addClass('roo-card-rotated');
2681 this.footerEl.addClass('d-none');
2682 this.el.select('.roo-collapsable').removeClass('show');
2684 this.rotated = true;
2685 this.fireEvent('rotate', this, this.rotated);
2689 dropPlaceHolder: function (action, info, data)
2691 if (this.dropEl === false) {
2692 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2696 this.dropEl.removeClass(['d-none', 'd-block']);
2697 if (action == 'hide') {
2699 this.dropEl.addClass('d-none');
2702 // FIXME - info.card == true!!!
2703 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2705 if (info.card !== true) {
2706 var cardel = info.card.el.dom;
2708 if (info.position == 'above') {
2709 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2710 } else if (cardel.nextSibling) {
2711 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2713 cardel.parentNode.append(this.dropEl.dom);
2716 // card container???
2717 this.containerEl.dom.append(this.dropEl.dom);
2720 this.dropEl.addClass('d-block roo-card-dropzone');
2722 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2729 setHeaderText: function(html)
2732 if (this.headerContainerEl) {
2733 this.headerContainerEl.dom.innerHTML = html;
2736 onHeaderImageLoad : function(ev, he)
2738 if (!this.header_image_fit_square) {
2742 var hw = he.naturalHeight / he.naturalWidth;
2745 //var w = he.dom.naturalWidth;
2748 he.style.position = 'relative';
2750 var nw = (ww * (1/hw));
2751 Roo.get(he).setSize( ww * (1/hw), ww);
2752 he.style.left = ((ww - nw)/ 2) + 'px';
2753 he.style.position = 'relative';
2764 * Card header - holder for the card header elements.
2769 * @class Roo.bootstrap.CardHeader
2770 * @extends Roo.bootstrap.Element
2771 * Bootstrap CardHeader class
2773 * Create a new Card Header - that you can embed children into
2774 * @param {Object} config The config object
2777 Roo.bootstrap.CardHeader = function(config){
2778 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2781 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2784 container_method : 'getCardHeader'
2797 * Card footer - holder for the card footer elements.
2802 * @class Roo.bootstrap.CardFooter
2803 * @extends Roo.bootstrap.Element
2804 * Bootstrap CardFooter class
2806 * Create a new Card Footer - that you can embed children into
2807 * @param {Object} config The config object
2810 Roo.bootstrap.CardFooter = function(config){
2811 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2814 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2817 container_method : 'getCardFooter'
2830 * Card header - holder for the card header elements.
2835 * @class Roo.bootstrap.CardImageTop
2836 * @extends Roo.bootstrap.Element
2837 * Bootstrap CardImageTop class
2839 * Create a new Card Image Top container
2840 * @param {Object} config The config object
2843 Roo.bootstrap.CardImageTop = function(config){
2844 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2847 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2850 container_method : 'getCardImageTop'
2865 * @class Roo.bootstrap.ButtonUploader
2866 * @extends Roo.bootstrap.Button
2867 * Bootstrap Button Uploader class - it's a button which when you add files to it
2870 * @cfg {Number} errorTimeout default 3000
2871 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2872 * @cfg {Array} html The button text.
2873 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2876 * Create a new CardUploader
2877 * @param {Object} config The config object
2880 Roo.bootstrap.ButtonUploader = function(config){
2884 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2890 * @event beforeselect
2891 * When button is pressed, before show upload files dialog is shown
2892 * @param {Roo.bootstrap.UploaderButton} this
2895 'beforeselect' : true,
2897 * @event fired when files have been selected,
2898 * When a the download link is clicked
2899 * @param {Roo.bootstrap.UploaderButton} this
2900 * @param {Array} Array of files that have been uploaded
2907 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2910 errorTimeout : 3000,
2914 fileCollection : false,
2919 getAutoCreate : function()
2924 cls : 'd-none roo-card-upload-selector'
2927 if (this.multiple) {
2928 im.multiple = 'multiple';
2934 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2944 initEvents : function()
2947 Roo.bootstrap.Button.prototype.initEvents.call(this);
2953 this.urlAPI = (window.createObjectURL && window) ||
2954 (window.URL && URL.revokeObjectURL && URL) ||
2955 (window.webkitURL && webkitURL);
2960 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2962 this.selectorEl.on('change', this.onFileSelected, this);
2969 onClick : function(e)
2973 if ( this.fireEvent('beforeselect', this) === false) {
2977 this.selectorEl.dom.click();
2981 onFileSelected : function(e)
2985 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2988 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2989 this.selectorEl.dom.value = '';// hopefully reset..
2991 this.fireEvent('uploaded', this, files );
2999 * addCard - add an Attachment to the uploader
3000 * @param data - the data about the image to upload
3004 title : "Title of file",
3005 is_uploaded : false,
3006 src : "http://.....",
3007 srcfile : { the File upload object },
3008 mimetype : file.type,
3011 .. any other data...
3036 * @class Roo.bootstrap.Img
3037 * @extends Roo.bootstrap.Component
3038 * Bootstrap Img class
3039 * @cfg {Boolean} imgResponsive false | true
3040 * @cfg {String} border rounded | circle | thumbnail
3041 * @cfg {String} src image source
3042 * @cfg {String} alt image alternative text
3043 * @cfg {String} href a tag href
3044 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3045 * @cfg {String} xsUrl xs image source
3046 * @cfg {String} smUrl sm image source
3047 * @cfg {String} mdUrl md image source
3048 * @cfg {String} lgUrl lg image source
3049 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3052 * Create a new Input
3053 * @param {Object} config The config object
3056 Roo.bootstrap.Img = function(config){
3057 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3063 * The img click event for the img.
3064 * @param {Roo.EventObject} e
3069 * The when any image loads
3070 * @param {Roo.EventObject} e
3076 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3078 imgResponsive: true,
3087 backgroundContain : false,
3089 getAutoCreate : function()
3091 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3092 return this.createSingleImg();
3097 cls: 'roo-image-responsive-group',
3102 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3104 if(!_this[size + 'Url']){
3110 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3111 html: _this.html || cfg.html,
3112 src: _this[size + 'Url']
3115 img.cls += ' roo-image-responsive-' + size;
3117 var s = ['xs', 'sm', 'md', 'lg'];
3119 s.splice(s.indexOf(size), 1);
3121 Roo.each(s, function(ss){
3122 img.cls += ' hidden-' + ss;
3125 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3126 cfg.cls += ' img-' + _this.border;
3130 cfg.alt = _this.alt;
3143 a.target = _this.target;
3147 cfg.cn.push((_this.href) ? a : img);
3154 createSingleImg : function()
3158 cls: (this.imgResponsive) ? 'img-responsive' : '',
3160 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3163 if (this.backgroundContain) {
3164 cfg.cls += ' background-contain';
3167 cfg.html = this.html || cfg.html;
3169 if (this.backgroundContain) {
3170 cfg.style="background-image: url(" + this.src + ')';
3172 cfg.src = this.src || cfg.src;
3175 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3176 cfg.cls += ' img-' + this.border;
3193 a.target = this.target;
3198 return (this.href) ? a : cfg;
3201 initEvents: function()
3204 this.el.on('click', this.onClick, this);
3206 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3207 this.el.on('load', this.onImageLoad, this);
3209 // not sure if this works.. not tested
3210 this.el.select('img', true).on('load', this.onImageLoad, this);
3215 onClick : function(e)
3217 Roo.log('img onclick');
3218 this.fireEvent('click', this, e);
3220 onImageLoad: function(e)
3222 Roo.log('img load');
3223 this.fireEvent('load', this, e);
3227 * Sets the url of the image - used to update it
3228 * @param {String} url the url of the image
3231 setSrc : function(url)
3235 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3236 if (this.backgroundContain) {
3237 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3239 this.el.dom.src = url;
3244 this.el.select('img', true).first().dom.src = url;
3260 * @class Roo.bootstrap.Link
3261 * @extends Roo.bootstrap.Component
3262 * Bootstrap Link Class
3263 * @cfg {String} alt image alternative text
3264 * @cfg {String} href a tag href
3265 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3266 * @cfg {String} html the content of the link.
3267 * @cfg {String} anchor name for the anchor link
3268 * @cfg {String} fa - favicon
3270 * @cfg {Boolean} preventDefault (true | false) default false
3274 * Create a new Input
3275 * @param {Object} config The config object
3278 Roo.bootstrap.Link = function(config){
3279 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3285 * The img click event for the img.
3286 * @param {Roo.EventObject} e
3292 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3296 preventDefault: false,
3302 getAutoCreate : function()
3304 var html = this.html || '';
3306 if (this.fa !== false) {
3307 html = '<i class="fa fa-' + this.fa + '"></i>';
3312 // anchor's do not require html/href...
3313 if (this.anchor === false) {
3315 cfg.href = this.href || '#';
3317 cfg.name = this.anchor;
3318 if (this.html !== false || this.fa !== false) {
3321 if (this.href !== false) {
3322 cfg.href = this.href;
3326 if(this.alt !== false){
3331 if(this.target !== false) {
3332 cfg.target = this.target;
3338 initEvents: function() {
3340 if(!this.href || this.preventDefault){
3341 this.el.on('click', this.onClick, this);
3345 onClick : function(e)
3347 if(this.preventDefault){
3350 //Roo.log('img onclick');
3351 this.fireEvent('click', this, e);
3364 * @class Roo.bootstrap.Header
3365 * @extends Roo.bootstrap.Component
3366 * Bootstrap Header class
3367 * @cfg {String} html content of header
3368 * @cfg {Number} level (1|2|3|4|5|6) default 1
3371 * Create a new Header
3372 * @param {Object} config The config object
3376 Roo.bootstrap.Header = function(config){
3377 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3380 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3388 getAutoCreate : function(){
3393 tag: 'h' + (1 *this.level),
3394 html: this.html || ''
3406 * Ext JS Library 1.1.1
3407 * Copyright(c) 2006-2007, Ext JS, LLC.
3409 * Originally Released Under LGPL - original licence link has changed is not relivant.
3412 * <script type="text/javascript">
3416 * @class Roo.bootstrap.MenuMgr
3417 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3420 Roo.bootstrap.MenuMgr = function(){
3421 var menus, active, groups = {}, attached = false, lastShow = new Date();
3423 // private - called when first menu is created
3426 active = new Roo.util.MixedCollection();
3427 Roo.get(document).addKeyListener(27, function(){
3428 if(active.length > 0){
3436 if(active && active.length > 0){
3437 var c = active.clone();
3447 if(active.length < 1){
3448 Roo.get(document).un("mouseup", onMouseDown);
3456 var last = active.last();
3457 lastShow = new Date();
3460 Roo.get(document).on("mouseup", onMouseDown);
3465 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3466 m.parentMenu.activeChild = m;
3467 }else if(last && last.isVisible()){
3468 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3473 function onBeforeHide(m){
3475 m.activeChild.hide();
3477 if(m.autoHideTimer){
3478 clearTimeout(m.autoHideTimer);
3479 delete m.autoHideTimer;
3484 function onBeforeShow(m){
3485 var pm = m.parentMenu;
3486 if(!pm && !m.allowOtherMenus){
3488 }else if(pm && pm.activeChild && active != m){
3489 pm.activeChild.hide();
3493 // private this should really trigger on mouseup..
3494 function onMouseDown(e){
3495 Roo.log("on Mouse Up");
3497 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3498 Roo.log("MenuManager hideAll");
3507 function onBeforeCheck(mi, state){
3509 var g = groups[mi.group];
3510 for(var i = 0, l = g.length; i < l; i++){
3512 g[i].setChecked(false);
3521 * Hides all menus that are currently visible
3523 hideAll : function(){
3528 register : function(menu){
3532 menus[menu.id] = menu;
3533 menu.on("beforehide", onBeforeHide);
3534 menu.on("hide", onHide);
3535 menu.on("beforeshow", onBeforeShow);
3536 menu.on("show", onShow);
3538 if(g && menu.events["checkchange"]){
3542 groups[g].push(menu);
3543 menu.on("checkchange", onCheck);
3548 * Returns a {@link Roo.menu.Menu} object
3549 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3550 * be used to generate and return a new Menu instance.
3552 get : function(menu){
3553 if(typeof menu == "string"){ // menu id
3555 }else if(menu.events){ // menu instance
3558 /*else if(typeof menu.length == 'number'){ // array of menu items?
3559 return new Roo.bootstrap.Menu({items:menu});
3560 }else{ // otherwise, must be a config
3561 return new Roo.bootstrap.Menu(menu);
3568 unregister : function(menu){
3569 delete menus[menu.id];
3570 menu.un("beforehide", onBeforeHide);
3571 menu.un("hide", onHide);
3572 menu.un("beforeshow", onBeforeShow);
3573 menu.un("show", onShow);
3575 if(g && menu.events["checkchange"]){
3576 groups[g].remove(menu);
3577 menu.un("checkchange", onCheck);
3582 registerCheckable : function(menuItem){
3583 var g = menuItem.group;
3588 groups[g].push(menuItem);
3589 menuItem.on("beforecheckchange", onBeforeCheck);
3594 unregisterCheckable : function(menuItem){
3595 var g = menuItem.group;
3597 groups[g].remove(menuItem);
3598 menuItem.un("beforecheckchange", onBeforeCheck);
3610 * @class Roo.bootstrap.Menu
3611 * @extends Roo.bootstrap.Component
3612 * Bootstrap Menu class - container for MenuItems
3613 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3614 * @cfg {bool} hidden if the menu should be hidden when rendered.
3615 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3616 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3617 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3618 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3622 * @param {Object} config The config object
3626 Roo.bootstrap.Menu = function(config){
3628 if (config.type == 'treeview') {
3629 // normally menu's are drawn attached to the document to handle layering etc..
3630 // however treeview (used by the docs menu is drawn into the parent element)
3631 this.container_method = 'getChildContainer';
3634 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3635 if (this.registerMenu && this.type != 'treeview') {
3636 Roo.bootstrap.MenuMgr.register(this);
3643 * Fires before this menu is displayed (return false to block)
3644 * @param {Roo.menu.Menu} this
3649 * Fires before this menu is hidden (return false to block)
3650 * @param {Roo.menu.Menu} this
3655 * Fires after this menu is displayed
3656 * @param {Roo.menu.Menu} this
3661 * Fires after this menu is hidden
3662 * @param {Roo.menu.Menu} this
3667 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3668 * @param {Roo.menu.Menu} this
3669 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3670 * @param {Roo.EventObject} e
3675 * Fires when the mouse is hovering over this menu
3676 * @param {Roo.menu.Menu} this
3677 * @param {Roo.EventObject} e
3678 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3683 * Fires when the mouse exits this menu
3684 * @param {Roo.menu.Menu} this
3685 * @param {Roo.EventObject} e
3686 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3691 * Fires when a menu item contained in this menu is clicked
3692 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3693 * @param {Roo.EventObject} e
3697 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3700 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3704 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3707 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3709 registerMenu : true,
3711 menuItems :false, // stores the menu items..
3721 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3723 hideTrigger : false,
3728 getChildContainer : function() {
3732 getAutoCreate : function(){
3734 //if (['right'].indexOf(this.align)!==-1) {
3735 // cfg.cn[1].cls += ' pull-right'
3740 cls : 'dropdown-menu shadow' ,
3741 style : 'z-index:1000'
3745 if (this.type === 'submenu') {
3746 cfg.cls = 'submenu active';
3748 if (this.type === 'treeview') {
3749 cfg.cls = 'treeview-menu';
3754 initEvents : function() {
3756 // Roo.log("ADD event");
3757 // Roo.log(this.triggerEl.dom);
3758 if (this.triggerEl) {
3760 this.triggerEl.on('click', this.onTriggerClick, this);
3762 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3764 if (!this.hideTrigger) {
3765 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3766 // dropdown toggle on the 'a' in BS4?
3767 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3769 this.triggerEl.addClass('dropdown-toggle');
3775 this.el.on('touchstart' , this.onTouch, this);
3777 this.el.on('click' , this.onClick, this);
3779 this.el.on("mouseover", this.onMouseOver, this);
3780 this.el.on("mouseout", this.onMouseOut, this);
3784 findTargetItem : function(e)
3786 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3790 //Roo.log(t); Roo.log(t.id);
3792 //Roo.log(this.menuitems);
3793 return this.menuitems.get(t.id);
3795 //return this.items.get(t.menuItemId);
3801 onTouch : function(e)
3803 Roo.log("menu.onTouch");
3804 //e.stopEvent(); this make the user popdown broken
3808 onClick : function(e)
3810 Roo.log("menu.onClick");
3812 var t = this.findTargetItem(e);
3813 if(!t || t.isContainer){
3818 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3819 if(t == this.activeItem && t.shouldDeactivate(e)){
3820 this.activeItem.deactivate();
3821 delete this.activeItem;
3825 this.setActiveItem(t, true);
3833 Roo.log('pass click event');
3837 this.fireEvent("click", this, t, e);
3841 if(!t.href.length || t.href == '#'){
3842 (function() { _this.hide(); }).defer(100);
3847 onMouseOver : function(e){
3848 var t = this.findTargetItem(e);
3851 // if(t.canActivate && !t.disabled){
3852 // this.setActiveItem(t, true);
3856 this.fireEvent("mouseover", this, e, t);
3858 isVisible : function(){
3859 return !this.hidden;
3861 onMouseOut : function(e){
3862 var t = this.findTargetItem(e);
3865 // if(t == this.activeItem && t.shouldDeactivate(e)){
3866 // this.activeItem.deactivate();
3867 // delete this.activeItem;
3870 this.fireEvent("mouseout", this, e, t);
3875 * Displays this menu relative to another element
3876 * @param {String/HTMLElement/Roo.Element} element The element to align to
3877 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3878 * the element (defaults to this.defaultAlign)
3879 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3881 show : function(el, pos, parentMenu)
3883 if (false === this.fireEvent("beforeshow", this)) {
3884 Roo.log("show canceled");
3887 this.parentMenu = parentMenu;
3891 this.el.addClass('show'); // show otherwise we do not know how big we are..
3893 var xy = this.el.getAlignToXY(el, pos);
3895 // bl-tl << left align below
3896 // tl-bl << left align
3898 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3899 // if it goes to far to the right.. -> align left.
3900 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3903 // was left align - go right?
3904 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3907 // goes down the bottom
3908 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3910 var a = this.align.replace('?', '').split('-');
3911 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3915 this.showAt( xy , parentMenu, false);
3918 * Displays this menu at a specific xy position
3919 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3920 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3922 showAt : function(xy, parentMenu, /* private: */_e){
3923 this.parentMenu = parentMenu;
3928 this.fireEvent("beforeshow", this);
3929 //xy = this.el.adjustForConstraints(xy);
3933 this.hideMenuItems();
3934 this.hidden = false;
3935 if (this.triggerEl) {
3936 this.triggerEl.addClass('open');
3939 this.el.addClass('show');
3943 // reassign x when hitting right
3945 // reassign y when hitting bottom
3947 // but the list may align on trigger left or trigger top... should it be a properity?
3949 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3954 this.fireEvent("show", this);
3960 this.doFocus.defer(50, this);
3964 doFocus : function(){
3966 this.focusEl.focus();
3971 * Hides this menu and optionally all parent menus
3972 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3974 hide : function(deep)
3976 if (false === this.fireEvent("beforehide", this)) {
3977 Roo.log("hide canceled");
3980 this.hideMenuItems();
3981 if(this.el && this.isVisible()){
3983 if(this.activeItem){
3984 this.activeItem.deactivate();
3985 this.activeItem = null;
3987 if (this.triggerEl) {
3988 this.triggerEl.removeClass('open');
3991 this.el.removeClass('show');
3993 this.fireEvent("hide", this);
3995 if(deep === true && this.parentMenu){
3996 this.parentMenu.hide(true);
4000 onTriggerClick : function(e)
4002 Roo.log('trigger click');
4004 var target = e.getTarget();
4006 Roo.log(target.nodeName.toLowerCase());
4008 if(target.nodeName.toLowerCase() === 'i'){
4014 onTriggerPress : function(e)
4016 Roo.log('trigger press');
4017 //Roo.log(e.getTarget());
4018 // Roo.log(this.triggerEl.dom);
4020 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4021 var pel = Roo.get(e.getTarget());
4022 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4023 Roo.log('is treeview or dropdown?');
4027 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4031 if (this.isVisible()) {
4037 this.show(this.triggerEl, this.align, false);
4040 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4047 hideMenuItems : function()
4049 Roo.log("hide Menu Items");
4054 this.el.select('.open',true).each(function(aa) {
4056 aa.removeClass('open');
4060 addxtypeChild : function (tree, cntr) {
4061 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4063 this.menuitems.add(comp);
4075 this.getEl().dom.innerHTML = '';
4076 this.menuitems.clear();
4090 * @class Roo.bootstrap.MenuItem
4091 * @extends Roo.bootstrap.Component
4092 * Bootstrap MenuItem class
4093 * @cfg {String} html the menu label
4094 * @cfg {String} href the link
4095 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4096 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4097 * @cfg {Boolean} active used on sidebars to highlight active itesm
4098 * @cfg {String} fa favicon to show on left of menu item.
4099 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4103 * Create a new MenuItem
4104 * @param {Object} config The config object
4108 Roo.bootstrap.MenuItem = function(config){
4109 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4114 * The raw click event for the entire grid.
4115 * @param {Roo.bootstrap.MenuItem} this
4116 * @param {Roo.EventObject} e
4122 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4126 preventDefault: false,
4127 isContainer : false,
4131 getAutoCreate : function(){
4133 if(this.isContainer){
4136 cls: 'dropdown-menu-item '
4146 cls : 'dropdown-item',
4151 if (this.fa !== false) {
4154 cls : 'fa fa-' + this.fa
4163 cls: 'dropdown-menu-item',
4166 if (this.parent().type == 'treeview') {
4167 cfg.cls = 'treeview-menu';
4170 cfg.cls += ' active';
4175 anc.href = this.href || cfg.cn[0].href ;
4176 ctag.html = this.html || cfg.cn[0].html ;
4180 initEvents: function()
4182 if (this.parent().type == 'treeview') {
4183 this.el.select('a').on('click', this.onClick, this);
4187 this.menu.parentType = this.xtype;
4188 this.menu.triggerEl = this.el;
4189 this.menu = this.addxtype(Roo.apply({}, this.menu));
4193 onClick : function(e)
4195 Roo.log('item on click ');
4197 if(this.preventDefault){
4200 //this.parent().hideMenuItems();
4202 this.fireEvent('click', this, e);
4221 * @class Roo.bootstrap.MenuSeparator
4222 * @extends Roo.bootstrap.Component
4223 * Bootstrap MenuSeparator class
4226 * Create a new MenuItem
4227 * @param {Object} config The config object
4231 Roo.bootstrap.MenuSeparator = function(config){
4232 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4235 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4237 getAutoCreate : function(){
4256 * @class Roo.bootstrap.Modal
4257 * @extends Roo.bootstrap.Component
4259 * Bootstrap Modal class
4260 * @cfg {String} title Title of dialog
4261 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4262 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4263 * @cfg {Boolean} specificTitle default false
4264 * @cfg {Array} buttons Array of buttons or standard button set..
4265 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4266 * @cfg {Boolean} animate default true
4267 * @cfg {Boolean} allow_close default true
4268 * @cfg {Boolean} fitwindow default false
4269 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4270 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4271 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4272 * @cfg {String} size (sm|lg|xl) default empty
4273 * @cfg {Number} max_width set the max width of modal
4274 * @cfg {Boolean} editableTitle can the title be edited
4279 * Create a new Modal Dialog
4280 * @param {Object} config The config object
4283 Roo.bootstrap.Modal = function(config){
4284 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4289 * The raw btnclick event for the button
4290 * @param {Roo.EventObject} e
4295 * Fire when dialog resize
4296 * @param {Roo.bootstrap.Modal} this
4297 * @param {Roo.EventObject} e
4301 * @event titlechanged
4302 * Fire when the editable title has been changed
4303 * @param {Roo.bootstrap.Modal} this
4304 * @param {Roo.EventObject} value
4306 "titlechanged" : true
4309 this.buttons = this.buttons || [];
4312 this.tmpl = Roo.factory(this.tmpl);
4317 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4319 title : 'test dialog',
4329 specificTitle: false,
4331 buttonPosition: 'right',
4353 editableTitle : false,
4355 onRender : function(ct, position)
4357 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360 var cfg = Roo.apply({}, this.getAutoCreate());
4363 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4365 //if (!cfg.name.length) {
4369 cfg.cls += ' ' + this.cls;
4372 cfg.style = this.style;
4374 this.el = Roo.get(document.body).createChild(cfg, position);
4376 //var type = this.el.dom.type;
4379 if(this.tabIndex !== undefined){
4380 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383 this.dialogEl = this.el.select('.modal-dialog',true).first();
4384 this.bodyEl = this.el.select('.modal-body',true).first();
4385 this.closeEl = this.el.select('.modal-header .close', true).first();
4386 this.headerEl = this.el.select('.modal-header',true).first();
4387 this.titleEl = this.el.select('.modal-title',true).first();
4388 this.footerEl = this.el.select('.modal-footer',true).first();
4390 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4392 //this.el.addClass("x-dlg-modal");
4394 if (this.buttons.length) {
4395 Roo.each(this.buttons, function(bb) {
4396 var b = Roo.apply({}, bb);
4397 b.xns = b.xns || Roo.bootstrap;
4398 b.xtype = b.xtype || 'Button';
4399 if (typeof(b.listeners) == 'undefined') {
4400 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4403 var btn = Roo.factory(b);
4405 btn.render(this.getButtonContainer());
4409 // render the children.
4412 if(typeof(this.items) != 'undefined'){
4413 var items = this.items;
4416 for(var i =0;i < items.length;i++) {
4417 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4421 this.items = nitems;
4423 // where are these used - they used to be body/close/footer
4427 //this.el.addClass([this.fieldClass, this.cls]);
4431 getAutoCreate : function()
4433 // we will default to modal-body-overflow - might need to remove or make optional later.
4435 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4436 html : this.html || ''
4441 cls : 'modal-title',
4445 if(this.specificTitle){ // WTF is this?
4450 if (this.allow_close && Roo.bootstrap.version == 3) {
4460 if (this.editableTitle) {
4462 cls: 'form-control roo-editable-title d-none',
4468 if (this.allow_close && Roo.bootstrap.version == 4) {
4478 if(this.size.length){
4479 size = 'modal-' + this.size;
4482 var footer = Roo.bootstrap.version == 3 ?
4484 cls : 'modal-footer',
4488 cls: 'btn-' + this.buttonPosition
4493 { // BS4 uses mr-auto on left buttons....
4494 cls : 'modal-footer'
4505 cls: "modal-dialog " + size,
4508 cls : "modal-content",
4511 cls : 'modal-header',
4526 modal.cls += ' fade';
4532 getChildContainer : function() {
4537 getButtonContainer : function() {
4539 return Roo.bootstrap.version == 4 ?
4540 this.el.select('.modal-footer',true).first()
4541 : this.el.select('.modal-footer div',true).first();
4544 initEvents : function()
4546 if (this.allow_close) {
4547 this.closeEl.on('click', this.hide, this);
4549 Roo.EventManager.onWindowResize(this.resize, this, true);
4550 if (this.editableTitle) {
4551 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4552 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4553 this.headerEditEl.on('keyup', function(e) {
4554 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4555 this.toggleHeaderInput(false)
4558 this.headerEditEl.on('blur', function(e) {
4559 this.toggleHeaderInput(false)
4568 this.maskEl.setSize(
4569 Roo.lib.Dom.getViewWidth(true),
4570 Roo.lib.Dom.getViewHeight(true)
4573 if (this.fitwindow) {
4575 this.dialogEl.setStyle( { 'max-width' : '100%' });
4577 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4578 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583 if(this.max_width !== 0) {
4585 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4588 this.setSize(w, this.height);
4592 if(this.max_height) {
4593 this.setSize(w,Math.min(
4595 Roo.lib.Dom.getViewportHeight(true) - 60
4601 if(!this.fit_content) {
4602 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4606 this.setSize(w, Math.min(
4608 this.headerEl.getHeight() +
4609 this.footerEl.getHeight() +
4610 this.getChildHeight(this.bodyEl.dom.childNodes),
4611 Roo.lib.Dom.getViewportHeight(true) - 60)
4617 setSize : function(w,h)
4628 if (!this.rendered) {
4631 this.toggleHeaderInput(false);
4632 //this.el.setStyle('display', 'block');
4633 this.el.removeClass('hideing');
4634 this.el.dom.style.display='block';
4636 Roo.get(document.body).addClass('modal-open');
4638 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4641 this.el.addClass('show');
4642 this.el.addClass('in');
4645 this.el.addClass('show');
4646 this.el.addClass('in');
4649 // not sure how we can show data in here..
4651 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4654 Roo.get(document.body).addClass("x-body-masked");
4656 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4657 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4658 this.maskEl.dom.style.display = 'block';
4659 this.maskEl.addClass('show');
4664 this.fireEvent('show', this);
4666 // set zindex here - otherwise it appears to be ignored...
4667 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670 this.items.forEach( function(e) {
4671 e.layout ? e.layout() : false;
4679 if(this.fireEvent("beforehide", this) !== false){
4681 this.maskEl.removeClass('show');
4683 this.maskEl.dom.style.display = '';
4684 Roo.get(document.body).removeClass("x-body-masked");
4685 this.el.removeClass('in');
4686 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4688 if(this.animate){ // why
4689 this.el.addClass('hideing');
4690 this.el.removeClass('show');
4692 if (!this.el.hasClass('hideing')) {
4693 return; // it's been shown again...
4696 this.el.dom.style.display='';
4698 Roo.get(document.body).removeClass('modal-open');
4699 this.el.removeClass('hideing');
4703 this.el.removeClass('show');
4704 this.el.dom.style.display='';
4705 Roo.get(document.body).removeClass('modal-open');
4708 this.fireEvent('hide', this);
4711 isVisible : function()
4714 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4718 addButton : function(str, cb)
4722 var b = Roo.apply({}, { html : str } );
4723 b.xns = b.xns || Roo.bootstrap;
4724 b.xtype = b.xtype || 'Button';
4725 if (typeof(b.listeners) == 'undefined') {
4726 b.listeners = { click : cb.createDelegate(this) };
4729 var btn = Roo.factory(b);
4731 btn.render(this.getButtonContainer());
4737 setDefaultButton : function(btn)
4739 //this.el.select('.modal-footer').()
4742 resizeTo: function(w,h)
4744 this.dialogEl.setWidth(w);
4746 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4748 this.bodyEl.setHeight(h - diff);
4750 this.fireEvent('resize', this);
4753 setContentSize : function(w, h)
4757 onButtonClick: function(btn,e)
4760 this.fireEvent('btnclick', btn.name, e);
4763 * Set the title of the Dialog
4764 * @param {String} str new Title
4766 setTitle: function(str) {
4767 this.titleEl.dom.innerHTML = str;
4771 * Set the body of the Dialog
4772 * @param {String} str new Title
4774 setBody: function(str) {
4775 this.bodyEl.dom.innerHTML = str;
4778 * Set the body of the Dialog using the template
4779 * @param {Obj} data - apply this data to the template and replace the body contents.
4781 applyBody: function(obj)
4784 Roo.log("Error - using apply Body without a template");
4787 this.tmpl.overwrite(this.bodyEl, obj);
4790 getChildHeight : function(child_nodes)
4794 child_nodes.length == 0
4799 var child_height = 0;
4801 for(var i = 0; i < child_nodes.length; i++) {
4804 * for modal with tabs...
4805 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4807 var layout_childs = child_nodes[i].childNodes;
4809 for(var j = 0; j < layout_childs.length; j++) {
4811 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4813 var layout_body_childs = layout_childs[j].childNodes;
4815 for(var k = 0; k < layout_body_childs.length; k++) {
4817 if(layout_body_childs[k].classList.contains('navbar')) {
4818 child_height += layout_body_childs[k].offsetHeight;
4822 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4824 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4826 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4828 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4829 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4844 child_height += child_nodes[i].offsetHeight;
4845 // Roo.log(child_nodes[i].offsetHeight);
4848 return child_height;
4850 toggleHeaderInput : function(is_edit)
4852 if (!this.editableTitle) {
4853 return; // not editable.
4855 if (is_edit && this.is_header_editing) {
4856 return; // already editing..
4860 this.headerEditEl.dom.value = this.title;
4861 this.headerEditEl.removeClass('d-none');
4862 this.headerEditEl.dom.focus();
4863 this.titleEl.addClass('d-none');
4865 this.is_header_editing = true;
4868 // flip back to not editing.
4869 this.title = this.headerEditEl.dom.value;
4870 this.headerEditEl.addClass('d-none');
4871 this.titleEl.removeClass('d-none');
4872 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4873 this.is_header_editing = false;
4874 this.fireEvent('titlechanged', this, this.title);
4883 Roo.apply(Roo.bootstrap.Modal, {
4885 * Button config that displays a single OK button
4894 * Button config that displays Yes and No buttons
4910 * Button config that displays OK and Cancel buttons
4925 * Button config that displays Yes, No and Cancel buttons
4950 * messagebox - can be used as a replace
4954 * @class Roo.MessageBox
4955 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4959 Roo.Msg.alert('Status', 'Changes saved successfully.');
4961 // Prompt for user data:
4962 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4964 // process text value...
4968 // Show a dialog using config options:
4970 title:'Save Changes?',
4971 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4972 buttons: Roo.Msg.YESNOCANCEL,
4979 Roo.bootstrap.MessageBox = function(){
4980 var dlg, opt, mask, waitTimer;
4981 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4982 var buttons, activeTextEl, bwidth;
4986 var handleButton = function(button){
4988 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4992 var handleHide = function(){
4994 dlg.el.removeClass(opt.cls);
4997 // Roo.TaskMgr.stop(waitTimer);
4998 // waitTimer = null;
5003 var updateButtons = function(b){
5006 buttons["ok"].hide();
5007 buttons["cancel"].hide();
5008 buttons["yes"].hide();
5009 buttons["no"].hide();
5010 dlg.footerEl.hide();
5014 dlg.footerEl.show();
5015 for(var k in buttons){
5016 if(typeof buttons[k] != "function"){
5019 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5020 width += buttons[k].el.getWidth()+15;
5030 var handleEsc = function(d, k, e){
5031 if(opt && opt.closable !== false){
5041 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5042 * @return {Roo.BasicDialog} The BasicDialog element
5044 getDialog : function(){
5046 dlg = new Roo.bootstrap.Modal( {
5049 //constraintoviewport:false,
5051 //collapsible : false,
5056 //buttonAlign:"center",
5057 closeClick : function(){
5058 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5061 handleButton("cancel");
5066 dlg.on("hide", handleHide);
5068 //dlg.addKeyListener(27, handleEsc);
5070 this.buttons = buttons;
5071 var bt = this.buttonText;
5072 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5073 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5074 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5075 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5077 bodyEl = dlg.bodyEl.createChild({
5079 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5080 '<textarea class="roo-mb-textarea"></textarea>' +
5081 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5083 msgEl = bodyEl.dom.firstChild;
5084 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5085 textboxEl.enableDisplayMode();
5086 textboxEl.addKeyListener([10,13], function(){
5087 if(dlg.isVisible() && opt && opt.buttons){
5090 }else if(opt.buttons.yes){
5091 handleButton("yes");
5095 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5096 textareaEl.enableDisplayMode();
5097 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5098 progressEl.enableDisplayMode();
5100 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5101 var pf = progressEl.dom.firstChild;
5103 pp = Roo.get(pf.firstChild);
5104 pp.setHeight(pf.offsetHeight);
5112 * Updates the message box body text
5113 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5114 * the XHTML-compliant non-breaking space character '&#160;')
5115 * @return {Roo.MessageBox} This message box
5117 updateText : function(text)
5119 if(!dlg.isVisible() && !opt.width){
5120 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5121 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5123 msgEl.innerHTML = text || ' ';
5125 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5126 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5128 Math.min(opt.width || cw , this.maxWidth),
5129 Math.max(opt.minWidth || this.minWidth, bwidth)
5132 activeTextEl.setWidth(w);
5134 if(dlg.isVisible()){
5135 dlg.fixedcenter = false;
5137 // to big, make it scroll. = But as usual stupid IE does not support
5140 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5141 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5142 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5144 bodyEl.dom.style.height = '';
5145 bodyEl.dom.style.overflowY = '';
5148 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5150 bodyEl.dom.style.overflowX = '';
5153 dlg.setContentSize(w, bodyEl.getHeight());
5154 if(dlg.isVisible()){
5155 dlg.fixedcenter = true;
5161 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5162 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5163 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5164 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5165 * @return {Roo.MessageBox} This message box
5167 updateProgress : function(value, text){
5169 this.updateText(text);
5172 if (pp) { // weird bug on my firefox - for some reason this is not defined
5173 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5174 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5180 * Returns true if the message box is currently displayed
5181 * @return {Boolean} True if the message box is visible, else false
5183 isVisible : function(){
5184 return dlg && dlg.isVisible();
5188 * Hides the message box if it is displayed
5191 if(this.isVisible()){
5197 * Displays a new message box, or reinitializes an existing message box, based on the config options
5198 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5199 * The following config object properties are supported:
5201 Property Type Description
5202 ---------- --------------- ------------------------------------------------------------------------------------
5203 animEl String/Element An id or Element from which the message box should animate as it opens and
5204 closes (defaults to undefined)
5205 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5206 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5207 closable Boolean False to hide the top-right close button (defaults to true). Note that
5208 progress and wait dialogs will ignore this property and always hide the
5209 close button as they can only be closed programmatically.
5210 cls String A custom CSS class to apply to the message box element
5211 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5212 displayed (defaults to 75)
5213 fn Function A callback function to execute after closing the dialog. The arguments to the
5214 function will be btn (the name of the button that was clicked, if applicable,
5215 e.g. "ok"), and text (the value of the active text field, if applicable).
5216 Progress and wait dialogs will ignore this option since they do not respond to
5217 user actions and can only be closed programmatically, so any required function
5218 should be called by the same code after it closes the dialog.
5219 icon String A CSS class that provides a background image to be used as an icon for
5220 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5221 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5222 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5223 modal Boolean False to allow user interaction with the page while the message box is
5224 displayed (defaults to true)
5225 msg String A string that will replace the existing message box body text (defaults
5226 to the XHTML-compliant non-breaking space character ' ')
5227 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5228 progress Boolean True to display a progress bar (defaults to false)
5229 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5230 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5231 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5232 title String The title text
5233 value String The string value to set into the active textbox element if displayed
5234 wait Boolean True to display a progress bar (defaults to false)
5235 width Number The width of the dialog in pixels
5242 msg: 'Please enter your address:',
5244 buttons: Roo.MessageBox.OKCANCEL,
5247 animEl: 'addAddressBtn'
5250 * @param {Object} config Configuration options
5251 * @return {Roo.MessageBox} This message box
5253 show : function(options)
5256 // this causes nightmares if you show one dialog after another
5257 // especially on callbacks..
5259 if(this.isVisible()){
5262 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5263 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5264 Roo.log("New Dialog Message:" + options.msg )
5265 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5266 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5269 var d = this.getDialog();
5271 d.setTitle(opt.title || " ");
5272 d.closeEl.setDisplayed(opt.closable !== false);
5273 activeTextEl = textboxEl;
5274 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5279 textareaEl.setHeight(typeof opt.multiline == "number" ?
5280 opt.multiline : this.defaultTextHeight);
5281 activeTextEl = textareaEl;
5290 progressEl.setDisplayed(opt.progress === true);
5292 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5294 this.updateProgress(0);
5295 activeTextEl.dom.value = opt.value || "";
5297 dlg.setDefaultButton(activeTextEl);
5299 var bs = opt.buttons;
5303 }else if(bs && bs.yes){
5304 db = buttons["yes"];
5306 dlg.setDefaultButton(db);
5308 bwidth = updateButtons(opt.buttons);
5309 this.updateText(opt.msg);
5311 d.el.addClass(opt.cls);
5313 d.proxyDrag = opt.proxyDrag === true;
5314 d.modal = opt.modal !== false;
5315 d.mask = opt.modal !== false ? mask : false;
5317 // force it to the end of the z-index stack so it gets a cursor in FF
5318 document.body.appendChild(dlg.el.dom);
5319 d.animateTarget = null;
5320 d.show(options.animEl);
5326 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5327 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5328 * and closing the message box when the process is complete.
5329 * @param {String} title The title bar text
5330 * @param {String} msg The message box body text
5331 * @return {Roo.MessageBox} This message box
5333 progress : function(title, msg){
5340 minWidth: this.minProgressWidth,
5347 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5348 * If a callback function is passed it will be called after the user clicks the button, and the
5349 * id of the button that was clicked will be passed as the only parameter to the callback
5350 * (could also be the top-right close button).
5351 * @param {String} title The title bar text
5352 * @param {String} msg The message box body text
5353 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5354 * @param {Object} scope (optional) The scope of the callback function
5355 * @return {Roo.MessageBox} This message box
5357 alert : function(title, msg, fn, scope)
5372 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5373 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5374 * You are responsible for closing the message box when the process is complete.
5375 * @param {String} msg The message box body text
5376 * @param {String} title (optional) The title bar text
5377 * @return {Roo.MessageBox} This message box
5379 wait : function(msg, title){
5390 waitTimer = Roo.TaskMgr.start({
5392 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5400 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5401 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5402 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5403 * @param {String} title The title bar text
5404 * @param {String} msg The message box body text
5405 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5406 * @param {Object} scope (optional) The scope of the callback function
5407 * @return {Roo.MessageBox} This message box
5409 confirm : function(title, msg, fn, scope){
5413 buttons: this.YESNO,
5422 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5423 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5424 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5425 * (could also be the top-right close button) and the text that was entered will be passed as the two
5426 * parameters to the callback.
5427 * @param {String} title The title bar text
5428 * @param {String} msg The message box body text
5429 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5430 * @param {Object} scope (optional) The scope of the callback function
5431 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5432 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5433 * @return {Roo.MessageBox} This message box
5435 prompt : function(title, msg, fn, scope, multiline){
5439 buttons: this.OKCANCEL,
5444 multiline: multiline,
5451 * Button config that displays a single OK button
5456 * Button config that displays Yes and No buttons
5459 YESNO : {yes:true, no:true},
5461 * Button config that displays OK and Cancel buttons
5464 OKCANCEL : {ok:true, cancel:true},
5466 * Button config that displays Yes, No and Cancel buttons
5469 YESNOCANCEL : {yes:true, no:true, cancel:true},
5472 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5475 defaultTextHeight : 75,
5477 * The maximum width in pixels of the message box (defaults to 600)
5482 * The minimum width in pixels of the message box (defaults to 100)
5487 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5488 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5491 minProgressWidth : 250,
5493 * An object containing the default button text strings that can be overriden for localized language support.
5494 * Supported properties are: ok, cancel, yes and no.
5495 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5508 * Shorthand for {@link Roo.MessageBox}
5510 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5511 Roo.Msg = Roo.Msg || Roo.MessageBox;
5520 * @class Roo.bootstrap.Navbar
5521 * @extends Roo.bootstrap.Component
5522 * Bootstrap Navbar class
5525 * Create a new Navbar
5526 * @param {Object} config The config object
5530 Roo.bootstrap.Navbar = function(config){
5531 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5535 * @event beforetoggle
5536 * Fire before toggle the menu
5537 * @param {Roo.EventObject} e
5539 "beforetoggle" : true
5543 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5552 getAutoCreate : function(){
5555 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5559 initEvents :function ()
5561 //Roo.log(this.el.select('.navbar-toggle',true));
5562 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5569 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5571 var size = this.el.getSize();
5572 this.maskEl.setSize(size.width, size.height);
5573 this.maskEl.enableDisplayMode("block");
5582 getChildContainer : function()
5584 if (this.el && this.el.select('.collapse').getCount()) {
5585 return this.el.select('.collapse',true).first();
5600 onToggle : function()
5603 if(this.fireEvent('beforetoggle', this) === false){
5606 var ce = this.el.select('.navbar-collapse',true).first();
5608 if (!ce.hasClass('show')) {
5618 * Expand the navbar pulldown
5620 expand : function ()
5623 var ce = this.el.select('.navbar-collapse',true).first();
5624 if (ce.hasClass('collapsing')) {
5627 ce.dom.style.height = '';
5629 ce.addClass('in'); // old...
5630 ce.removeClass('collapse');
5631 ce.addClass('show');
5632 var h = ce.getHeight();
5634 ce.removeClass('show');
5635 // at this point we should be able to see it..
5636 ce.addClass('collapsing');
5638 ce.setHeight(0); // resize it ...
5639 ce.on('transitionend', function() {
5640 //Roo.log('done transition');
5641 ce.removeClass('collapsing');
5642 ce.addClass('show');
5643 ce.removeClass('collapse');
5645 ce.dom.style.height = '';
5646 }, this, { single: true} );
5648 ce.dom.scrollTop = 0;
5651 * Collapse the navbar pulldown
5653 collapse : function()
5655 var ce = this.el.select('.navbar-collapse',true).first();
5657 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5658 // it's collapsed or collapsing..
5661 ce.removeClass('in'); // old...
5662 ce.setHeight(ce.getHeight());
5663 ce.removeClass('show');
5664 ce.addClass('collapsing');
5666 ce.on('transitionend', function() {
5667 ce.dom.style.height = '';
5668 ce.removeClass('collapsing');
5669 ce.addClass('collapse');
5670 }, this, { single: true} );
5690 * @class Roo.bootstrap.NavSimplebar
5691 * @extends Roo.bootstrap.Navbar
5692 * Bootstrap Sidebar class
5694 * @cfg {Boolean} inverse is inverted color
5696 * @cfg {String} type (nav | pills | tabs)
5697 * @cfg {Boolean} arrangement stacked | justified
5698 * @cfg {String} align (left | right) alignment
5700 * @cfg {Boolean} main (true|false) main nav bar? default false
5701 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5703 * @cfg {String} tag (header|footer|nav|div) default is nav
5705 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5709 * Create a new Sidebar
5710 * @param {Object} config The config object
5714 Roo.bootstrap.NavSimplebar = function(config){
5715 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5718 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5734 getAutoCreate : function(){
5738 tag : this.tag || 'div',
5739 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5741 if (['light','white'].indexOf(this.weight) > -1) {
5742 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5744 cfg.cls += ' bg-' + this.weight;
5747 cfg.cls += ' navbar-inverse';
5751 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5753 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5762 cls: 'nav nav-' + this.xtype,
5768 this.type = this.type || 'nav';
5769 if (['tabs','pills'].indexOf(this.type) != -1) {
5770 cfg.cn[0].cls += ' nav-' + this.type
5774 if (this.type!=='nav') {
5775 Roo.log('nav type must be nav/tabs/pills')
5777 cfg.cn[0].cls += ' navbar-nav'
5783 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5784 cfg.cn[0].cls += ' nav-' + this.arrangement;
5788 if (this.align === 'right') {
5789 cfg.cn[0].cls += ' navbar-right';
5814 * navbar-expand-md fixed-top
5818 * @class Roo.bootstrap.NavHeaderbar
5819 * @extends Roo.bootstrap.NavSimplebar
5820 * Bootstrap Sidebar class
5822 * @cfg {String} brand what is brand
5823 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5824 * @cfg {String} brand_href href of the brand
5825 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5826 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5827 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5828 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5831 * Create a new Sidebar
5832 * @param {Object} config The config object
5836 Roo.bootstrap.NavHeaderbar = function(config){
5837 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5841 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5848 desktopCenter : false,
5851 getAutoCreate : function(){
5854 tag: this.nav || 'nav',
5855 cls: 'navbar navbar-expand-md',
5861 if (this.desktopCenter) {
5862 cn.push({cls : 'container', cn : []});
5870 cls: 'navbar-toggle navbar-toggler',
5871 'data-toggle': 'collapse',
5876 html: 'Toggle navigation'
5880 cls: 'icon-bar navbar-toggler-icon'
5893 cn.push( Roo.bootstrap.version == 4 ? btn : {
5895 cls: 'navbar-header',
5904 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5908 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5910 if (['light','white'].indexOf(this.weight) > -1) {
5911 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5913 cfg.cls += ' bg-' + this.weight;
5916 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5917 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5919 // tag can override this..
5921 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5924 if (this.brand !== '') {
5925 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5926 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5928 href: this.brand_href ? this.brand_href : '#',
5929 cls: 'navbar-brand',
5937 cfg.cls += ' main-nav';
5945 getHeaderChildContainer : function()
5947 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5948 return this.el.select('.navbar-header',true).first();
5951 return this.getChildContainer();
5954 getChildContainer : function()
5957 return this.el.select('.roo-navbar-collapse',true).first();
5962 initEvents : function()
5964 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5966 if (this.autohide) {
5971 Roo.get(document).on('scroll',function(e) {
5972 var ns = Roo.get(document).getScroll().top;
5973 var os = prevScroll;
5977 ft.removeClass('slideDown');
5978 ft.addClass('slideUp');
5981 ft.removeClass('slideUp');
5982 ft.addClass('slideDown');
6003 * @class Roo.bootstrap.NavSidebar
6004 * @extends Roo.bootstrap.Navbar
6005 * Bootstrap Sidebar class
6008 * Create a new Sidebar
6009 * @param {Object} config The config object
6013 Roo.bootstrap.NavSidebar = function(config){
6014 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6017 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6019 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6021 getAutoCreate : function(){
6026 cls: 'sidebar sidebar-nav'
6048 * @class Roo.bootstrap.NavGroup
6049 * @extends Roo.bootstrap.Component
6050 * Bootstrap NavGroup class
6051 * @cfg {String} align (left|right)
6052 * @cfg {Boolean} inverse
6053 * @cfg {String} type (nav|pills|tab) default nav
6054 * @cfg {String} navId - reference Id for navbar.
6055 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6058 * Create a new nav group
6059 * @param {Object} config The config object
6062 Roo.bootstrap.NavGroup = function(config){
6063 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6066 Roo.bootstrap.NavGroup.register(this);
6070 * Fires when the active item changes
6071 * @param {Roo.bootstrap.NavGroup} this
6072 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6073 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6080 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6092 getAutoCreate : function()
6094 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6100 if (Roo.bootstrap.version == 4) {
6101 if (['tabs','pills'].indexOf(this.type) != -1) {
6102 cfg.cls += ' nav-' + this.type;
6104 // trying to remove so header bar can right align top?
6105 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6106 // do not use on header bar...
6107 cfg.cls += ' navbar-nav';
6112 if (['tabs','pills'].indexOf(this.type) != -1) {
6113 cfg.cls += ' nav-' + this.type
6115 if (this.type !== 'nav') {
6116 Roo.log('nav type must be nav/tabs/pills')
6118 cfg.cls += ' navbar-nav'
6122 if (this.parent() && this.parent().sidebar) {
6125 cls: 'dashboard-menu sidebar-menu'
6131 if (this.form === true) {
6134 cls: 'navbar-form form-inline'
6136 //nav navbar-right ml-md-auto
6137 if (this.align === 'right') {
6138 cfg.cls += ' navbar-right ml-md-auto';
6140 cfg.cls += ' navbar-left';
6144 if (this.align === 'right') {
6145 cfg.cls += ' navbar-right ml-md-auto';
6147 cfg.cls += ' mr-auto';
6151 cfg.cls += ' navbar-inverse';
6159 * sets the active Navigation item
6160 * @param {Roo.bootstrap.NavItem} the new current navitem
6162 setActiveItem : function(item)
6165 Roo.each(this.navItems, function(v){
6170 v.setActive(false, true);
6177 item.setActive(true, true);
6178 this.fireEvent('changed', this, item, prev);
6183 * gets the active Navigation item
6184 * @return {Roo.bootstrap.NavItem} the current navitem
6186 getActive : function()
6190 Roo.each(this.navItems, function(v){
6201 indexOfNav : function()
6205 Roo.each(this.navItems, function(v,i){
6216 * adds a Navigation item
6217 * @param {Roo.bootstrap.NavItem} the navitem to add
6219 addItem : function(cfg)
6221 if (this.form && Roo.bootstrap.version == 4) {
6224 var cn = new Roo.bootstrap.NavItem(cfg);
6226 cn.parentId = this.id;
6227 cn.onRender(this.el, null);
6231 * register a Navigation item
6232 * @param {Roo.bootstrap.NavItem} the navitem to add
6234 register : function(item)
6236 this.navItems.push( item);
6237 item.navId = this.navId;
6242 * clear all the Navigation item
6245 clearAll : function()
6248 this.el.dom.innerHTML = '';
6251 getNavItem: function(tabId)
6254 Roo.each(this.navItems, function(e) {
6255 if (e.tabId == tabId) {
6265 setActiveNext : function()
6267 var i = this.indexOfNav(this.getActive());
6268 if (i > this.navItems.length) {
6271 this.setActiveItem(this.navItems[i+1]);
6273 setActivePrev : function()
6275 var i = this.indexOfNav(this.getActive());
6279 this.setActiveItem(this.navItems[i-1]);
6281 clearWasActive : function(except) {
6282 Roo.each(this.navItems, function(e) {
6283 if (e.tabId != except.tabId && e.was_active) {
6284 e.was_active = false;
6291 getWasActive : function ()
6294 Roo.each(this.navItems, function(e) {
6309 Roo.apply(Roo.bootstrap.NavGroup, {
6313 * register a Navigation Group
6314 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6316 register : function(navgrp)
6318 this.groups[navgrp.navId] = navgrp;
6322 * fetch a Navigation Group based on the navigation ID
6323 * @param {string} the navgroup to add
6324 * @returns {Roo.bootstrap.NavGroup} the navgroup
6326 get: function(navId) {
6327 if (typeof(this.groups[navId]) == 'undefined') {
6329 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6331 return this.groups[navId] ;
6346 * @class Roo.bootstrap.NavItem
6347 * @extends Roo.bootstrap.Component
6348 * Bootstrap Navbar.NavItem class
6349 * @cfg {String} href link to
6350 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6351 * @cfg {Boolean} button_outline show and outlined button
6352 * @cfg {String} html content of button
6353 * @cfg {String} badge text inside badge
6354 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6355 * @cfg {String} glyphicon DEPRICATED - use fa
6356 * @cfg {String} icon DEPRICATED - use fa
6357 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6358 * @cfg {Boolean} active Is item active
6359 * @cfg {Boolean} disabled Is item disabled
6360 * @cfg {String} linkcls Link Class
6361 * @cfg {Boolean} preventDefault (true | false) default false
6362 * @cfg {String} tabId the tab that this item activates.
6363 * @cfg {String} tagtype (a|span) render as a href or span?
6364 * @cfg {Boolean} animateRef (true|false) link to element default false
6367 * Create a new Navbar Item
6368 * @param {Object} config The config object
6370 Roo.bootstrap.NavItem = function(config){
6371 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6376 * The raw click event for the entire grid.
6377 * @param {Roo.EventObject} e
6382 * Fires when the active item active state changes
6383 * @param {Roo.bootstrap.NavItem} this
6384 * @param {boolean} state the new state
6390 * Fires when scroll to element
6391 * @param {Roo.bootstrap.NavItem} this
6392 * @param {Object} options
6393 * @param {Roo.EventObject} e
6401 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6410 preventDefault : false,
6418 button_outline : false,
6422 getAutoCreate : function(){
6429 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6432 cfg.cls += ' active' ;
6434 if (this.disabled) {
6435 cfg.cls += ' disabled';
6439 if (this.button_weight.length) {
6440 cfg.tag = this.href ? 'a' : 'button';
6441 cfg.html = this.html || '';
6442 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6444 cfg.href = this.href;
6447 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6449 cfg.cls += " nav-html";
6452 // menu .. should add dropdown-menu class - so no need for carat..
6454 if (this.badge !== '') {
6456 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6461 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6465 href : this.href || "#",
6466 html: this.html || '',
6470 if (this.tagtype == 'a') {
6471 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6475 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6476 } else if (this.fa) {
6477 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6478 } else if(this.glyphicon) {
6479 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6481 cfg.cn[0].cls += " nav-html";
6485 cfg.cn[0].html += " <span class='caret'></span>";
6489 if (this.badge !== '') {
6490 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6498 onRender : function(ct, position)
6500 // Roo.log("Call onRender: " + this.xtype);
6501 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6505 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6506 this.navLink = this.el.select('.nav-link',true).first();
6507 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6512 initEvents: function()
6514 if (typeof (this.menu) != 'undefined') {
6515 this.menu.parentType = this.xtype;
6516 this.menu.triggerEl = this.el;
6517 this.menu = this.addxtype(Roo.apply({}, this.menu));
6520 this.el.on('click', this.onClick, this);
6522 //if(this.tagtype == 'span'){
6523 // this.el.select('span',true).on('click', this.onClick, this);
6526 // at this point parent should be available..
6527 this.parent().register(this);
6530 onClick : function(e)
6532 if (e.getTarget('.dropdown-menu-item')) {
6533 // did you click on a menu itemm.... - then don't trigger onclick..
6538 this.preventDefault ||
6541 Roo.log("NavItem - prevent Default?");
6545 if (this.disabled) {
6549 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6550 if (tg && tg.transition) {
6551 Roo.log("waiting for the transitionend");
6557 //Roo.log("fire event clicked");
6558 if(this.fireEvent('click', this, e) === false){
6562 if(this.tagtype == 'span'){
6566 //Roo.log(this.href);
6567 var ael = this.el.select('a',true).first();
6570 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6571 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6572 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6573 return; // ignore... - it's a 'hash' to another page.
6575 Roo.log("NavItem - prevent Default?");
6577 this.scrollToElement(e);
6581 var p = this.parent();
6583 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6584 if (typeof(p.setActiveItem) !== 'undefined') {
6585 p.setActiveItem(this);
6589 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6590 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6591 // remove the collapsed menu expand...
6592 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6596 isActive: function () {
6599 setActive : function(state, fire, is_was_active)
6601 if (this.active && !state && this.navId) {
6602 this.was_active = true;
6603 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6605 nv.clearWasActive(this);
6609 this.active = state;
6612 this.el.removeClass('active');
6613 this.navLink ? this.navLink.removeClass('active') : false;
6614 } else if (!this.el.hasClass('active')) {
6616 this.el.addClass('active');
6617 if (Roo.bootstrap.version == 4 && this.navLink ) {
6618 this.navLink.addClass('active');
6623 this.fireEvent('changed', this, state);
6626 // show a panel if it's registered and related..
6628 if (!this.navId || !this.tabId || !state || is_was_active) {
6632 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6636 var pan = tg.getPanelByName(this.tabId);
6640 // if we can not flip to new panel - go back to old nav highlight..
6641 if (false == tg.showPanel(pan)) {
6642 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6644 var onav = nv.getWasActive();
6646 onav.setActive(true, false, true);
6655 // this should not be here...
6656 setDisabled : function(state)
6658 this.disabled = state;
6660 this.el.removeClass('disabled');
6661 } else if (!this.el.hasClass('disabled')) {
6662 this.el.addClass('disabled');
6668 * Fetch the element to display the tooltip on.
6669 * @return {Roo.Element} defaults to this.el
6671 tooltipEl : function()
6673 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6676 scrollToElement : function(e)
6678 var c = document.body;
6681 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6683 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6684 c = document.documentElement;
6687 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6693 var o = target.calcOffsetsTo(c);
6700 this.fireEvent('scrollto', this, options, e);
6702 Roo.get(c).scrollTo('top', options.value, true);
6707 * Set the HTML (text content) of the item
6708 * @param {string} html content for the nav item
6710 setHtml : function(html)
6713 this.htmlEl.dom.innerHTML = html;
6725 * <span> icon </span>
6726 * <span> text </span>
6727 * <span>badge </span>
6731 * @class Roo.bootstrap.NavSidebarItem
6732 * @extends Roo.bootstrap.NavItem
6733 * Bootstrap Navbar.NavSidebarItem class
6734 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6735 * {Boolean} open is the menu open
6736 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6737 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6738 * {String} buttonSize (sm|md|lg)the extra classes for the button
6739 * {Boolean} showArrow show arrow next to the text (default true)
6741 * Create a new Navbar Button
6742 * @param {Object} config The config object
6744 Roo.bootstrap.NavSidebarItem = function(config){
6745 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6750 * The raw click event for the entire grid.
6751 * @param {Roo.EventObject} e
6756 * Fires when the active item active state changes
6757 * @param {Roo.bootstrap.NavSidebarItem} this
6758 * @param {boolean} state the new state
6766 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6768 badgeWeight : 'default',
6774 buttonWeight : 'default',
6780 getAutoCreate : function(){
6785 href : this.href || '#',
6791 if(this.buttonView){
6794 href : this.href || '#',
6795 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6808 cfg.cls += ' active';
6811 if (this.disabled) {
6812 cfg.cls += ' disabled';
6815 cfg.cls += ' open x-open';
6818 if (this.glyphicon || this.icon) {
6819 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6820 a.cn.push({ tag : 'i', cls : c }) ;
6823 if(!this.buttonView){
6826 html : this.html || ''
6833 if (this.badge !== '') {
6834 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6840 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6843 a.cls += ' dropdown-toggle treeview' ;
6849 initEvents : function()
6851 if (typeof (this.menu) != 'undefined') {
6852 this.menu.parentType = this.xtype;
6853 this.menu.triggerEl = this.el;
6854 this.menu = this.addxtype(Roo.apply({}, this.menu));
6857 this.el.on('click', this.onClick, this);
6859 if(this.badge !== ''){
6860 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6865 onClick : function(e)
6872 if(this.preventDefault){
6876 this.fireEvent('click', this, e);
6879 disable : function()
6881 this.setDisabled(true);
6886 this.setDisabled(false);
6889 setDisabled : function(state)
6891 if(this.disabled == state){
6895 this.disabled = state;
6898 this.el.addClass('disabled');
6902 this.el.removeClass('disabled');
6907 setActive : function(state)
6909 if(this.active == state){
6913 this.active = state;
6916 this.el.addClass('active');
6920 this.el.removeClass('active');
6925 isActive: function ()
6930 setBadge : function(str)
6936 this.badgeEl.dom.innerHTML = str;
6951 Roo.namespace('Roo.bootstrap.breadcrumb');
6955 * @class Roo.bootstrap.breadcrumb.Nav
6956 * @extends Roo.bootstrap.Component
6957 * Bootstrap Breadcrumb Nav Class
6959 * @children Roo.bootstrap.breadcrumb.Item
6962 * Create a new breadcrumb.Nav
6963 * @param {Object} config The config object
6967 Roo.bootstrap.breadcrumb.Nav = function(config){
6968 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6973 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6975 getAutoCreate : function()
6992 initEvents: function()
6994 this.olEl = this.el.select('ol',true).first();
6996 getChildContainer : function()
7012 * @class Roo.bootstrap.breadcrumb.Nav
7013 * @extends Roo.bootstrap.Component
7014 * Bootstrap Breadcrumb Nav Class
7016 * @children Roo.bootstrap.breadcrumb.Component
7017 * @cfg {String} html the content of the link.
7018 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7019 * @cfg {Boolean} active is it active
7023 * Create a new breadcrumb.Nav
7024 * @param {Object} config The config object
7027 Roo.bootstrap.breadcrumb.Item = function(config){
7028 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7033 * The img click event for the img.
7034 * @param {Roo.EventObject} e
7041 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7046 getAutoCreate : function()
7051 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7053 if (this.href !== false) {
7060 cfg.html = this.html;
7066 initEvents: function()
7069 this.el.select('a', true).first().on('click',this.onClick, this)
7073 onClick : function(e)
7076 this.fireEvent('click',this, e);
7089 * @class Roo.bootstrap.Row
7090 * @extends Roo.bootstrap.Component
7091 * Bootstrap Row class (contains columns...)
7095 * @param {Object} config The config object
7098 Roo.bootstrap.Row = function(config){
7099 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7102 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7104 getAutoCreate : function(){
7123 * @class Roo.bootstrap.Pagination
7124 * @extends Roo.bootstrap.Component
7125 * Bootstrap Pagination class
7126 * @cfg {String} size xs | sm | md | lg
7127 * @cfg {Boolean} inverse false | true
7130 * Create a new Pagination
7131 * @param {Object} config The config object
7134 Roo.bootstrap.Pagination = function(config){
7135 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7138 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7144 getAutoCreate : function(){
7150 cfg.cls += ' inverse';
7156 cfg.cls += " " + this.cls;
7174 * @class Roo.bootstrap.PaginationItem
7175 * @extends Roo.bootstrap.Component
7176 * Bootstrap PaginationItem class
7177 * @cfg {String} html text
7178 * @cfg {String} href the link
7179 * @cfg {Boolean} preventDefault (true | false) default true
7180 * @cfg {Boolean} active (true | false) default false
7181 * @cfg {Boolean} disabled default false
7185 * Create a new PaginationItem
7186 * @param {Object} config The config object
7190 Roo.bootstrap.PaginationItem = function(config){
7191 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7196 * The raw click event for the entire grid.
7197 * @param {Roo.EventObject} e
7203 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7207 preventDefault: true,
7212 getAutoCreate : function(){
7218 href : this.href ? this.href : '#',
7219 html : this.html ? this.html : ''
7229 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7233 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7239 initEvents: function() {
7241 this.el.on('click', this.onClick, this);
7244 onClick : function(e)
7246 Roo.log('PaginationItem on click ');
7247 if(this.preventDefault){
7255 this.fireEvent('click', this, e);
7271 * @class Roo.bootstrap.Slider
7272 * @extends Roo.bootstrap.Component
7273 * Bootstrap Slider class
7276 * Create a new Slider
7277 * @param {Object} config The config object
7280 Roo.bootstrap.Slider = function(config){
7281 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7284 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7286 getAutoCreate : function(){
7290 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7294 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7306 * Ext JS Library 1.1.1
7307 * Copyright(c) 2006-2007, Ext JS, LLC.
7309 * Originally Released Under LGPL - original licence link has changed is not relivant.
7312 * <script type="text/javascript">
7315 * @extends Roo.dd.DDProxy
7316 * @class Roo.grid.SplitDragZone
7317 * Support for Column Header resizing
7319 * @param {Object} config
7322 // This is a support class used internally by the Grid components
7323 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7325 this.view = grid.getView();
7326 this.proxy = this.view.resizeProxy;
7327 Roo.grid.SplitDragZone.superclass.constructor.call(
7330 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7332 dragElId : Roo.id(this.proxy.dom),
7337 this.setHandleElId(Roo.id(hd));
7338 if (hd2 !== false) {
7339 this.setOuterHandleElId(Roo.id(hd2));
7342 this.scroll = false;
7344 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7345 fly: Roo.Element.fly,
7347 b4StartDrag : function(x, y){
7348 this.view.headersDisabled = true;
7349 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7350 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7352 this.proxy.setHeight(h);
7354 // for old system colWidth really stored the actual width?
7355 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7356 // which in reality did not work.. - it worked only for fixed sizes
7357 // for resizable we need to use actual sizes.
7358 var w = this.cm.getColumnWidth(this.cellIndex);
7359 if (!this.view.mainWrap) {
7361 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7366 // this was w-this.grid.minColumnWidth;
7367 // doesnt really make sense? - w = thie curren width or the rendered one?
7368 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7369 this.resetConstraints();
7370 this.setXConstraint(minw, 1000);
7371 this.setYConstraint(0, 0);
7372 this.minX = x - minw;
7373 this.maxX = x + 1000;
7375 if (!this.view.mainWrap) { // this is Bootstrap code..
7376 this.getDragEl().style.display='block';
7379 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7383 handleMouseDown : function(e){
7384 ev = Roo.EventObject.setEvent(e);
7385 var t = this.fly(ev.getTarget());
7386 if(t.hasClass("x-grid-split")){
7387 this.cellIndex = this.view.getCellIndex(t.dom);
7389 this.cm = this.grid.colModel;
7390 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7391 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7396 endDrag : function(e){
7397 this.view.headersDisabled = false;
7398 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7399 var diff = endX - this.startPos;
7401 var w = this.cm.getColumnWidth(this.cellIndex);
7402 if (!this.view.mainWrap) {
7405 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7408 autoOffset : function(){
7413 * Ext JS Library 1.1.1
7414 * Copyright(c) 2006-2007, Ext JS, LLC.
7416 * Originally Released Under LGPL - original licence link has changed is not relivant.
7419 * <script type="text/javascript">
7423 * @class Roo.grid.AbstractSelectionModel
7424 * @extends Roo.util.Observable
7426 * Abstract base class for grid SelectionModels. It provides the interface that should be
7427 * implemented by descendant classes. This class should not be directly instantiated.
7430 Roo.grid.AbstractSelectionModel = function(){
7431 this.locked = false;
7432 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7435 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7436 /** @ignore Called by the grid automatically. Do not call directly. */
7437 init : function(grid){
7443 * Locks the selections.
7450 * Unlocks the selections.
7452 unlock : function(){
7453 this.locked = false;
7457 * Returns true if the selections are locked.
7460 isLocked : function(){
7465 * Ext JS Library 1.1.1
7466 * Copyright(c) 2006-2007, Ext JS, LLC.
7468 * Originally Released Under LGPL - original licence link has changed is not relivant.
7471 * <script type="text/javascript">
7474 * @extends Roo.grid.AbstractSelectionModel
7475 * @class Roo.grid.RowSelectionModel
7476 * The default SelectionModel used by {@link Roo.grid.Grid}.
7477 * It supports multiple selections and keyboard selection/navigation.
7479 * @param {Object} config
7481 Roo.grid.RowSelectionModel = function(config){
7482 Roo.apply(this, config);
7483 this.selections = new Roo.util.MixedCollection(false, function(o){
7488 this.lastActive = false;
7492 * @event selectionchange
7493 * Fires when the selection changes
7494 * @param {SelectionModel} this
7496 "selectionchange" : true,
7498 * @event afterselectionchange
7499 * Fires after the selection changes (eg. by key press or clicking)
7500 * @param {SelectionModel} this
7502 "afterselectionchange" : true,
7504 * @event beforerowselect
7505 * Fires when a row is selected being selected, return false to cancel.
7506 * @param {SelectionModel} this
7507 * @param {Number} rowIndex The selected index
7508 * @param {Boolean} keepExisting False if other selections will be cleared
7510 "beforerowselect" : true,
7513 * Fires when a row is selected.
7514 * @param {SelectionModel} this
7515 * @param {Number} rowIndex The selected index
7516 * @param {Roo.data.Record} r The record
7520 * @event rowdeselect
7521 * Fires when a row is deselected.
7522 * @param {SelectionModel} this
7523 * @param {Number} rowIndex The selected index
7525 "rowdeselect" : true
7527 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7528 this.locked = false;
7531 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7533 * @cfg {Boolean} singleSelect
7534 * True to allow selection of only one row at a time (defaults to false)
7536 singleSelect : false,
7539 initEvents : function(){
7541 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7542 this.grid.on("mousedown", this.handleMouseDown, this);
7543 }else{ // allow click to work like normal
7544 this.grid.on("rowclick", this.handleDragableRowClick, this);
7546 // bootstrap does not have a view..
7547 var view = this.grid.view ? this.grid.view : this.grid;
7548 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7551 this.selectPrevious(e.shiftKey);
7552 }else if(this.last !== false && this.lastActive !== false){
7553 var last = this.last;
7554 this.selectRange(this.last, this.lastActive-1);
7555 view.focusRow(this.lastActive);
7560 this.selectFirstRow();
7562 this.fireEvent("afterselectionchange", this);
7564 "down" : function(e){
7566 this.selectNext(e.shiftKey);
7567 }else if(this.last !== false && this.lastActive !== false){
7568 var last = this.last;
7569 this.selectRange(this.last, this.lastActive+1);
7570 view.focusRow(this.lastActive);
7575 this.selectFirstRow();
7577 this.fireEvent("afterselectionchange", this);
7583 view.on("refresh", this.onRefresh, this);
7584 view.on("rowupdated", this.onRowUpdated, this);
7585 view.on("rowremoved", this.onRemove, this);
7589 onRefresh : function(){
7590 var ds = this.grid.ds, i, v = this.grid.view;
7591 var s = this.selections;
7593 if((i = ds.indexOfId(r.id)) != -1){
7595 s.add(ds.getAt(i)); // updating the selection relate data
7603 onRemove : function(v, index, r){
7604 this.selections.remove(r);
7608 onRowUpdated : function(v, index, r){
7609 if(this.isSelected(r)){
7610 v.onRowSelect(index);
7616 * @param {Array} records The records to select
7617 * @param {Boolean} keepExisting (optional) True to keep existing selections
7619 selectRecords : function(records, keepExisting){
7621 this.clearSelections();
7623 var ds = this.grid.ds;
7624 for(var i = 0, len = records.length; i < len; i++){
7625 this.selectRow(ds.indexOf(records[i]), true);
7630 * Gets the number of selected rows.
7633 getCount : function(){
7634 return this.selections.length;
7638 * Selects the first row in the grid.
7640 selectFirstRow : function(){
7645 * Select the last row.
7646 * @param {Boolean} keepExisting (optional) True to keep existing selections
7648 selectLastRow : function(keepExisting){
7649 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7653 * Selects the row immediately following the last selected row.
7654 * @param {Boolean} keepExisting (optional) True to keep existing selections
7656 selectNext : function(keepExisting){
7657 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7658 this.selectRow(this.last+1, keepExisting);
7659 var view = this.grid.view ? this.grid.view : this.grid;
7660 view.focusRow(this.last);
7665 * Selects the row that precedes the last selected row.
7666 * @param {Boolean} keepExisting (optional) True to keep existing selections
7668 selectPrevious : function(keepExisting){
7670 this.selectRow(this.last-1, keepExisting);
7671 var view = this.grid.view ? this.grid.view : this.grid;
7672 view.focusRow(this.last);
7677 * Returns the selected records
7678 * @return {Array} Array of selected records
7680 getSelections : function(){
7681 return [].concat(this.selections.items);
7685 * Returns the first selected record.
7688 getSelected : function(){
7689 return this.selections.itemAt(0);
7694 * Clears all selections.
7696 clearSelections : function(fast){
7701 var ds = this.grid.ds;
7702 var s = this.selections;
7704 this.deselectRow(ds.indexOfId(r.id));
7708 this.selections.clear();
7717 selectAll : function(){
7721 this.selections.clear();
7722 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7723 this.selectRow(i, true);
7728 * Returns True if there is a selection.
7731 hasSelection : function(){
7732 return this.selections.length > 0;
7736 * Returns True if the specified row is selected.
7737 * @param {Number/Record} record The record or index of the record to check
7740 isSelected : function(index){
7741 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7742 return (r && this.selections.key(r.id) ? true : false);
7746 * Returns True if the specified record id is selected.
7747 * @param {String} id The id of record to check
7750 isIdSelected : function(id){
7751 return (this.selections.key(id) ? true : false);
7755 handleMouseDown : function(e, t)
7757 var view = this.grid.view ? this.grid.view : this.grid;
7759 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7762 if(e.shiftKey && this.last !== false){
7763 var last = this.last;
7764 this.selectRange(last, rowIndex, e.ctrlKey);
7765 this.last = last; // reset the last
7766 view.focusRow(rowIndex);
7768 var isSelected = this.isSelected(rowIndex);
7769 if(e.button !== 0 && isSelected){
7770 view.focusRow(rowIndex);
7771 }else if(e.ctrlKey && isSelected){
7772 this.deselectRow(rowIndex);
7773 }else if(!isSelected){
7774 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7775 view.focusRow(rowIndex);
7778 this.fireEvent("afterselectionchange", this);
7781 handleDragableRowClick : function(grid, rowIndex, e)
7783 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7784 this.selectRow(rowIndex, false);
7785 var view = this.grid.view ? this.grid.view : this.grid;
7786 view.focusRow(rowIndex);
7787 this.fireEvent("afterselectionchange", this);
7792 * Selects multiple rows.
7793 * @param {Array} rows Array of the indexes of the row to select
7794 * @param {Boolean} keepExisting (optional) True to keep existing selections
7796 selectRows : function(rows, keepExisting){
7798 this.clearSelections();
7800 for(var i = 0, len = rows.length; i < len; i++){
7801 this.selectRow(rows[i], true);
7806 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7807 * @param {Number} startRow The index of the first row in the range
7808 * @param {Number} endRow The index of the last row in the range
7809 * @param {Boolean} keepExisting (optional) True to retain existing selections
7811 selectRange : function(startRow, endRow, keepExisting){
7816 this.clearSelections();
7818 if(startRow <= endRow){
7819 for(var i = startRow; i <= endRow; i++){
7820 this.selectRow(i, true);
7823 for(var i = startRow; i >= endRow; i--){
7824 this.selectRow(i, true);
7830 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7831 * @param {Number} startRow The index of the first row in the range
7832 * @param {Number} endRow The index of the last row in the range
7834 deselectRange : function(startRow, endRow, preventViewNotify){
7838 for(var i = startRow; i <= endRow; i++){
7839 this.deselectRow(i, preventViewNotify);
7845 * @param {Number} row The index of the row to select
7846 * @param {Boolean} keepExisting (optional) True to keep existing selections
7848 selectRow : function(index, keepExisting, preventViewNotify){
7849 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7852 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7853 if(!keepExisting || this.singleSelect){
7854 this.clearSelections();
7856 var r = this.grid.ds.getAt(index);
7857 this.selections.add(r);
7858 this.last = this.lastActive = index;
7859 if(!preventViewNotify){
7860 var view = this.grid.view ? this.grid.view : this.grid;
7861 view.onRowSelect(index);
7863 this.fireEvent("rowselect", this, index, r);
7864 this.fireEvent("selectionchange", this);
7870 * @param {Number} row The index of the row to deselect
7872 deselectRow : function(index, preventViewNotify){
7876 if(this.last == index){
7879 if(this.lastActive == index){
7880 this.lastActive = false;
7882 var r = this.grid.ds.getAt(index);
7883 this.selections.remove(r);
7884 if(!preventViewNotify){
7885 var view = this.grid.view ? this.grid.view : this.grid;
7886 view.onRowDeselect(index);
7888 this.fireEvent("rowdeselect", this, index);
7889 this.fireEvent("selectionchange", this);
7893 restoreLast : function(){
7895 this.last = this._last;
7900 acceptsNav : function(row, col, cm){
7901 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7905 onEditorKey : function(field, e){
7906 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7911 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7913 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7915 }else if(k == e.ENTER && !e.ctrlKey){
7919 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7921 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7923 }else if(k == e.ESC){
7927 g.startEditing(newCell[0], newCell[1]);
7932 * Ext JS Library 1.1.1
7933 * Copyright(c) 2006-2007, Ext JS, LLC.
7935 * Originally Released Under LGPL - original licence link has changed is not relivant.
7938 * <script type="text/javascript">
7943 * @class Roo.grid.ColumnModel
7944 * @extends Roo.util.Observable
7945 * This is the default implementation of a ColumnModel used by the Grid. It defines
7946 * the columns in the grid.
7949 var colModel = new Roo.grid.ColumnModel([
7950 {header: "Ticker", width: 60, sortable: true, locked: true},
7951 {header: "Company Name", width: 150, sortable: true},
7952 {header: "Market Cap.", width: 100, sortable: true},
7953 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7954 {header: "Employees", width: 100, sortable: true, resizable: false}
7959 * The config options listed for this class are options which may appear in each
7960 * individual column definition.
7961 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7963 * @param {Object} config An Array of column config objects. See this class's
7964 * config objects for details.
7966 Roo.grid.ColumnModel = function(config){
7968 * The config passed into the constructor
7970 this.config = []; //config;
7973 // if no id, create one
7974 // if the column does not have a dataIndex mapping,
7975 // map it to the order it is in the config
7976 for(var i = 0, len = config.length; i < len; i++){
7977 this.addColumn(config[i]);
7982 * The width of columns which have no width specified (defaults to 100)
7985 this.defaultWidth = 100;
7988 * Default sortable of columns which have no sortable specified (defaults to false)
7991 this.defaultSortable = false;
7995 * @event widthchange
7996 * Fires when the width of a column changes.
7997 * @param {ColumnModel} this
7998 * @param {Number} columnIndex The column index
7999 * @param {Number} newWidth The new width
8001 "widthchange": true,
8003 * @event headerchange
8004 * Fires when the text of a header changes.
8005 * @param {ColumnModel} this
8006 * @param {Number} columnIndex The column index
8007 * @param {Number} newText The new header text
8009 "headerchange": true,
8011 * @event hiddenchange
8012 * Fires when a column is hidden or "unhidden".
8013 * @param {ColumnModel} this
8014 * @param {Number} columnIndex The column index
8015 * @param {Boolean} hidden true if hidden, false otherwise
8017 "hiddenchange": true,
8019 * @event columnmoved
8020 * Fires when a column is moved.
8021 * @param {ColumnModel} this
8022 * @param {Number} oldIndex
8023 * @param {Number} newIndex
8025 "columnmoved" : true,
8027 * @event columlockchange
8028 * Fires when a column's locked state is changed
8029 * @param {ColumnModel} this
8030 * @param {Number} colIndex
8031 * @param {Boolean} locked true if locked
8033 "columnlockchange" : true
8035 Roo.grid.ColumnModel.superclass.constructor.call(this);
8037 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8039 * @cfg {String} header The header text to display in the Grid view.
8042 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8045 * @cfg {String} smHeader Header at Bootsrap Small width
8048 * @cfg {String} mdHeader Header at Bootsrap Medium width
8051 * @cfg {String} lgHeader Header at Bootsrap Large width
8054 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8057 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8058 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8059 * specified, the column's index is used as an index into the Record's data Array.
8062 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8063 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8066 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8067 * Defaults to the value of the {@link #defaultSortable} property.
8068 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8071 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8074 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8077 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8080 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8083 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8084 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8085 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8086 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8089 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8092 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8095 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8098 * @cfg {String} cursor (Optional)
8101 * @cfg {String} tooltip (Optional)
8104 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8107 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8110 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8113 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8116 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8119 * Returns the id of the column at the specified index.
8120 * @param {Number} index The column index
8121 * @return {String} the id
8123 getColumnId : function(index){
8124 return this.config[index].id;
8128 * Returns the column for a specified id.
8129 * @param {String} id The column id
8130 * @return {Object} the column
8132 getColumnById : function(id){
8133 return this.lookup[id];
8138 * Returns the column Object for a specified dataIndex.
8139 * @param {String} dataIndex The column dataIndex
8140 * @return {Object|Boolean} the column or false if not found
8142 getColumnByDataIndex: function(dataIndex){
8143 var index = this.findColumnIndex(dataIndex);
8144 return index > -1 ? this.config[index] : false;
8148 * Returns the index for a specified column id.
8149 * @param {String} id The column id
8150 * @return {Number} the index, or -1 if not found
8152 getIndexById : function(id){
8153 for(var i = 0, len = this.config.length; i < len; i++){
8154 if(this.config[i].id == id){
8162 * Returns the index for a specified column dataIndex.
8163 * @param {String} dataIndex The column dataIndex
8164 * @return {Number} the index, or -1 if not found
8167 findColumnIndex : function(dataIndex){
8168 for(var i = 0, len = this.config.length; i < len; i++){
8169 if(this.config[i].dataIndex == dataIndex){
8177 moveColumn : function(oldIndex, newIndex){
8178 var c = this.config[oldIndex];
8179 this.config.splice(oldIndex, 1);
8180 this.config.splice(newIndex, 0, c);
8181 this.dataMap = null;
8182 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8185 isLocked : function(colIndex){
8186 return this.config[colIndex].locked === true;
8189 setLocked : function(colIndex, value, suppressEvent){
8190 if(this.isLocked(colIndex) == value){
8193 this.config[colIndex].locked = value;
8195 this.fireEvent("columnlockchange", this, colIndex, value);
8199 getTotalLockedWidth : function(){
8201 for(var i = 0; i < this.config.length; i++){
8202 if(this.isLocked(i) && !this.isHidden(i)){
8203 this.totalWidth += this.getColumnWidth(i);
8209 getLockedCount : function(){
8210 for(var i = 0, len = this.config.length; i < len; i++){
8211 if(!this.isLocked(i)){
8216 return this.config.length;
8220 * Returns the number of columns.
8223 getColumnCount : function(visibleOnly){
8224 if(visibleOnly === true){
8226 for(var i = 0, len = this.config.length; i < len; i++){
8227 if(!this.isHidden(i)){
8233 return this.config.length;
8237 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8238 * @param {Function} fn
8239 * @param {Object} scope (optional)
8240 * @return {Array} result
8242 getColumnsBy : function(fn, scope){
8244 for(var i = 0, len = this.config.length; i < len; i++){
8245 var c = this.config[i];
8246 if(fn.call(scope||this, c, i) === true){
8254 * Returns true if the specified column is sortable.
8255 * @param {Number} col The column index
8258 isSortable : function(col){
8259 if(typeof this.config[col].sortable == "undefined"){
8260 return this.defaultSortable;
8262 return this.config[col].sortable;
8266 * Returns the rendering (formatting) function defined for the column.
8267 * @param {Number} col The column index.
8268 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8270 getRenderer : function(col){
8271 if(!this.config[col].renderer){
8272 return Roo.grid.ColumnModel.defaultRenderer;
8274 return this.config[col].renderer;
8278 * Sets the rendering (formatting) function for a column.
8279 * @param {Number} col The column index
8280 * @param {Function} fn The function to use to process the cell's raw data
8281 * to return HTML markup for the grid view. The render function is called with
8282 * the following parameters:<ul>
8283 * <li>Data value.</li>
8284 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8285 * <li>css A CSS style string to apply to the table cell.</li>
8286 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8287 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8288 * <li>Row index</li>
8289 * <li>Column index</li>
8290 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8292 setRenderer : function(col, fn){
8293 this.config[col].renderer = fn;
8297 * Returns the width for the specified column.
8298 * @param {Number} col The column index
8299 * @param (optional) {String} gridSize bootstrap width size.
8302 getColumnWidth : function(col, gridSize)
8304 var cfg = this.config[col];
8306 if (typeof(gridSize) == 'undefined') {
8307 return cfg.width * 1 || this.defaultWidth;
8309 if (gridSize === false) { // if we set it..
8310 return cfg.width || false;
8312 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8314 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8315 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8318 return cfg[ sizes[i] ];
8325 * Sets the width for a column.
8326 * @param {Number} col The column index
8327 * @param {Number} width The new width
8329 setColumnWidth : function(col, width, suppressEvent){
8330 this.config[col].width = width;
8331 this.totalWidth = null;
8333 this.fireEvent("widthchange", this, col, width);
8338 * Returns the total width of all columns.
8339 * @param {Boolean} includeHidden True to include hidden column widths
8342 getTotalWidth : function(includeHidden){
8343 if(!this.totalWidth){
8344 this.totalWidth = 0;
8345 for(var i = 0, len = this.config.length; i < len; i++){
8346 if(includeHidden || !this.isHidden(i)){
8347 this.totalWidth += this.getColumnWidth(i);
8351 return this.totalWidth;
8355 * Returns the header for the specified column.
8356 * @param {Number} col The column index
8359 getColumnHeader : function(col){
8360 return this.config[col].header;
8364 * Sets the header for a column.
8365 * @param {Number} col The column index
8366 * @param {String} header The new header
8368 setColumnHeader : function(col, header){
8369 this.config[col].header = header;
8370 this.fireEvent("headerchange", this, col, header);
8374 * Returns the tooltip for the specified column.
8375 * @param {Number} col The column index
8378 getColumnTooltip : function(col){
8379 return this.config[col].tooltip;
8382 * Sets the tooltip for a column.
8383 * @param {Number} col The column index
8384 * @param {String} tooltip The new tooltip
8386 setColumnTooltip : function(col, tooltip){
8387 this.config[col].tooltip = tooltip;
8391 * Returns the dataIndex for the specified column.
8392 * @param {Number} col The column index
8395 getDataIndex : function(col){
8396 return this.config[col].dataIndex;
8400 * Sets the dataIndex for a column.
8401 * @param {Number} col The column index
8402 * @param {Number} dataIndex The new dataIndex
8404 setDataIndex : function(col, dataIndex){
8405 this.config[col].dataIndex = dataIndex;
8411 * Returns true if the cell is editable.
8412 * @param {Number} colIndex The column index
8413 * @param {Number} rowIndex The row index - this is nto actually used..?
8416 isCellEditable : function(colIndex, rowIndex){
8417 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8421 * Returns the editor defined for the cell/column.
8422 * return false or null to disable editing.
8423 * @param {Number} colIndex The column index
8424 * @param {Number} rowIndex The row index
8427 getCellEditor : function(colIndex, rowIndex){
8428 return this.config[colIndex].editor;
8432 * Sets if a column is editable.
8433 * @param {Number} col The column index
8434 * @param {Boolean} editable True if the column is editable
8436 setEditable : function(col, editable){
8437 this.config[col].editable = editable;
8442 * Returns true if the column is hidden.
8443 * @param {Number} colIndex The column index
8446 isHidden : function(colIndex){
8447 return this.config[colIndex].hidden;
8452 * Returns true if the column width cannot be changed
8454 isFixed : function(colIndex){
8455 return this.config[colIndex].fixed;
8459 * Returns true if the column can be resized
8462 isResizable : function(colIndex){
8463 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8466 * Sets if a column is hidden.
8467 * @param {Number} colIndex The column index
8468 * @param {Boolean} hidden True if the column is hidden
8470 setHidden : function(colIndex, hidden){
8471 this.config[colIndex].hidden = hidden;
8472 this.totalWidth = null;
8473 this.fireEvent("hiddenchange", this, colIndex, hidden);
8477 * Sets the editor for a column.
8478 * @param {Number} col The column index
8479 * @param {Object} editor The editor object
8481 setEditor : function(col, editor){
8482 this.config[col].editor = editor;
8485 * Add a column (experimental...) - defaults to adding to the end..
8486 * @param {Object} config
8488 addColumn : function(c)
8491 var i = this.config.length;
8494 if(typeof c.dataIndex == "undefined"){
8497 if(typeof c.renderer == "string"){
8498 c.renderer = Roo.util.Format[c.renderer];
8500 if(typeof c.id == "undefined"){
8503 if(c.editor && c.editor.xtype){
8504 c.editor = Roo.factory(c.editor, Roo.grid);
8506 if(c.editor && c.editor.isFormField){
8507 c.editor = new Roo.grid.GridEditor(c.editor);
8509 this.lookup[c.id] = c;
8514 Roo.grid.ColumnModel.defaultRenderer = function(value)
8516 if(typeof value == "object") {
8519 if(typeof value == "string" && value.length < 1){
8523 return String.format("{0}", value);
8526 // Alias for backwards compatibility
8527 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8530 * Ext JS Library 1.1.1
8531 * Copyright(c) 2006-2007, Ext JS, LLC.
8533 * Originally Released Under LGPL - original licence link has changed is not relivant.
8536 * <script type="text/javascript">
8540 * @class Roo.LoadMask
8541 * A simple utility class for generically masking elements while loading data. If the element being masked has
8542 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8543 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8544 * element's UpdateManager load indicator and will be destroyed after the initial load.
8546 * Create a new LoadMask
8547 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8548 * @param {Object} config The config object
8550 Roo.LoadMask = function(el, config){
8551 this.el = Roo.get(el);
8552 Roo.apply(this, config);
8554 this.store.on('beforeload', this.onBeforeLoad, this);
8555 this.store.on('load', this.onLoad, this);
8556 this.store.on('loadexception', this.onLoadException, this);
8557 this.removeMask = false;
8559 var um = this.el.getUpdateManager();
8560 um.showLoadIndicator = false; // disable the default indicator
8561 um.on('beforeupdate', this.onBeforeLoad, this);
8562 um.on('update', this.onLoad, this);
8563 um.on('failure', this.onLoad, this);
8564 this.removeMask = true;
8568 Roo.LoadMask.prototype = {
8570 * @cfg {Boolean} removeMask
8571 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8572 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8577 * The text to display in a centered loading message box (defaults to 'Loading...')
8581 * @cfg {String} msgCls
8582 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8584 msgCls : 'x-mask-loading',
8587 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8593 * Disables the mask to prevent it from being displayed
8595 disable : function(){
8596 this.disabled = true;
8600 * Enables the mask so that it can be displayed
8602 enable : function(){
8603 this.disabled = false;
8606 onLoadException : function()
8610 if (typeof(arguments[3]) != 'undefined') {
8611 Roo.MessageBox.alert("Error loading",arguments[3]);
8615 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8616 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8623 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8628 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8632 onBeforeLoad : function(){
8634 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8639 destroy : function(){
8641 this.store.un('beforeload', this.onBeforeLoad, this);
8642 this.store.un('load', this.onLoad, this);
8643 this.store.un('loadexception', this.onLoadException, this);
8645 var um = this.el.getUpdateManager();
8646 um.un('beforeupdate', this.onBeforeLoad, this);
8647 um.un('update', this.onLoad, this);
8648 um.un('failure', this.onLoad, this);
8652 * @class Roo.bootstrap.Table
8654 * @extends Roo.bootstrap.Component
8655 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8656 * Similar to Roo.grid.Grid
8658 var table = Roo.factory({
8660 xns : Roo.bootstrap,
8661 autoSizeColumns: true,
8668 sortInfo : { direction : 'ASC', field: 'name' },
8670 xtype : 'HttpProxy',
8673 url : 'https://example.com/some.data.url.json'
8676 xtype : 'JsonReader',
8678 fields : [ 'id', 'name', whatever' ],
8685 xtype : 'ColumnModel',
8689 dataIndex : 'is_in_group',
8692 renderer : function(v, x , r) {
8694 return String.format("{0}", v)
8700 xtype : 'RowSelectionModel',
8701 xns : Roo.bootstrap.Table
8702 // you can add listeners to catch selection change here....
8708 grid.render(Roo.get("some-div"));
8711 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8716 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8717 * @cfg {Roo.data.Store} store The data store to use
8718 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8720 * @cfg {String} cls table class
8723 * @cfg {boolean} striped Should the rows be alternative striped
8724 * @cfg {boolean} bordered Add borders to the table
8725 * @cfg {boolean} hover Add hover highlighting
8726 * @cfg {boolean} condensed Format condensed
8727 * @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,
8728 * also adds table-responsive (see bootstrap docs for details)
8729 * @cfg {Boolean} loadMask (true|false) default false
8730 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8731 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8732 * @cfg {Boolean} rowSelection (true|false) default false
8733 * @cfg {Boolean} cellSelection (true|false) default false
8734 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8735 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8736 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8737 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8738 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8739 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8742 * Create a new Table
8743 * @param {Object} config The config object
8746 Roo.bootstrap.Table = function(config)
8748 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8751 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8752 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8753 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8754 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8756 this.view = this; // compat with grid.
8758 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8760 this.sm.grid = this;
8761 this.selModel = Roo.factory(this.sm, Roo.grid);
8762 this.sm = this.selModel;
8763 this.sm.xmodule = this.xmodule || false;
8766 if (this.cm && typeof(this.cm.config) == 'undefined') {
8767 this.colModel = new Roo.grid.ColumnModel(this.cm);
8768 this.cm = this.colModel;
8769 this.cm.xmodule = this.xmodule || false;
8772 this.store= Roo.factory(this.store, Roo.data);
8773 this.ds = this.store;
8774 this.ds.xmodule = this.xmodule || false;
8777 if (this.footer && this.store) {
8778 this.footer.dataSource = this.ds;
8779 this.footer = Roo.factory(this.footer);
8786 * Fires when a cell is clicked
8787 * @param {Roo.bootstrap.Table} this
8788 * @param {Roo.Element} el
8789 * @param {Number} rowIndex
8790 * @param {Number} columnIndex
8791 * @param {Roo.EventObject} e
8795 * @event celldblclick
8796 * Fires when a cell is double clicked
8797 * @param {Roo.bootstrap.Table} this
8798 * @param {Roo.Element} el
8799 * @param {Number} rowIndex
8800 * @param {Number} columnIndex
8801 * @param {Roo.EventObject} e
8803 "celldblclick" : true,
8806 * Fires when a row is clicked
8807 * @param {Roo.bootstrap.Table} this
8808 * @param {Roo.Element} el
8809 * @param {Number} rowIndex
8810 * @param {Roo.EventObject} e
8814 * @event rowdblclick
8815 * Fires when a row is double clicked
8816 * @param {Roo.bootstrap.Table} this
8817 * @param {Roo.Element} el
8818 * @param {Number} rowIndex
8819 * @param {Roo.EventObject} e
8821 "rowdblclick" : true,
8824 * Fires when a mouseover occur
8825 * @param {Roo.bootstrap.Table} this
8826 * @param {Roo.Element} el
8827 * @param {Number} rowIndex
8828 * @param {Number} columnIndex
8829 * @param {Roo.EventObject} e
8834 * Fires when a mouseout occur
8835 * @param {Roo.bootstrap.Table} this
8836 * @param {Roo.Element} el
8837 * @param {Number} rowIndex
8838 * @param {Number} columnIndex
8839 * @param {Roo.EventObject} e
8844 * Fires when a row is rendered, so you can change add a style to it.
8845 * @param {Roo.bootstrap.Table} this
8846 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8850 * @event rowsrendered
8851 * Fires when all the rows have been rendered
8852 * @param {Roo.bootstrap.Table} this
8854 'rowsrendered' : true,
8856 * @event contextmenu
8857 * The raw contextmenu event for the entire grid.
8858 * @param {Roo.EventObject} e
8860 "contextmenu" : true,
8862 * @event rowcontextmenu
8863 * Fires when a row is right clicked
8864 * @param {Roo.bootstrap.Table} this
8865 * @param {Number} rowIndex
8866 * @param {Roo.EventObject} e
8868 "rowcontextmenu" : true,
8870 * @event cellcontextmenu
8871 * Fires when a cell is right clicked
8872 * @param {Roo.bootstrap.Table} this
8873 * @param {Number} rowIndex
8874 * @param {Number} cellIndex
8875 * @param {Roo.EventObject} e
8877 "cellcontextmenu" : true,
8879 * @event headercontextmenu
8880 * Fires when a header is right clicked
8881 * @param {Roo.bootstrap.Table} this
8882 * @param {Number} columnIndex
8883 * @param {Roo.EventObject} e
8885 "headercontextmenu" : true,
8888 * The raw mousedown event for the entire grid.
8889 * @param {Roo.EventObject} e
8896 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8912 enableColumnResize: true,
8914 rowSelection : false,
8915 cellSelection : false,
8918 minColumnWidth : 50,
8920 // Roo.Element - the tbody
8921 bodyEl: false, // <tbody> Roo.Element - thead element
8922 headEl: false, // <thead> Roo.Element - thead element
8923 resizeProxy : false, // proxy element for dragging?
8927 container: false, // used by gridpanel...
8933 auto_hide_footer : false,
8935 view: false, // actually points to this..
8937 getAutoCreate : function()
8939 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8946 // this get's auto added by panel.Grid
8947 if (this.scrollBody) {
8948 cfg.cls += ' table-body-fixed';
8951 cfg.cls += ' table-striped';
8955 cfg.cls += ' table-hover';
8957 if (this.bordered) {
8958 cfg.cls += ' table-bordered';
8960 if (this.condensed) {
8961 cfg.cls += ' table-condensed';
8964 if (this.responsive) {
8965 cfg.cls += ' table-responsive';
8969 cfg.cls+= ' ' +this.cls;
8975 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8978 if(this.store || this.cm){
8979 if(this.headerShow){
8980 cfg.cn.push(this.renderHeader());
8983 cfg.cn.push(this.renderBody());
8985 if(this.footerShow){
8986 cfg.cn.push(this.renderFooter());
8988 // where does this come from?
8989 //cfg.cls+= ' TableGrid';
8992 return { cn : [ cfg ] };
8995 initEvents : function()
8997 if(!this.store || !this.cm){
9000 if (this.selModel) {
9001 this.selModel.initEvents();
9005 //Roo.log('initEvents with ds!!!!');
9007 this.bodyEl = this.el.select('tbody', true).first();
9008 this.headEl = this.el.select('thead', true).first();
9009 this.mainFoot = this.el.select('tfoot', true).first();
9014 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9015 e.on('click', this.sort, this);
9019 // why is this done????? = it breaks dialogs??
9020 //this.parent().el.setStyle('position', 'relative');
9024 this.footer.parentId = this.id;
9025 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9028 this.el.select('tfoot tr td').first().addClass('hide');
9033 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9036 this.store.on('load', this.onLoad, this);
9037 this.store.on('beforeload', this.onBeforeLoad, this);
9038 this.store.on('update', this.onUpdate, this);
9039 this.store.on('add', this.onAdd, this);
9040 this.store.on("clear", this.clear, this);
9042 this.el.on("contextmenu", this.onContextMenu, this);
9045 this.cm.on("headerchange", this.onHeaderChange, this);
9046 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9048 //?? does bodyEl get replaced on render?
9049 this.bodyEl.on("click", this.onClick, this);
9050 this.bodyEl.on("dblclick", this.onDblClick, this);
9051 this.bodyEl.on('scroll', this.onBodyScroll, this);
9053 // guessing mainbody will work - this relays usually caught by selmodel at present.
9054 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9057 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9060 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9061 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9066 // Compatibility with grid - we implement all the view features at present.
9067 getView : function()
9072 initCSS : function()
9076 var cm = this.cm, styles = [];
9077 this.CSS.removeStyleSheet(this.id + '-cssrules');
9078 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9079 // we can honour xs/sm/md/xl as widths...
9080 // we first have to decide what widht we are currently at...
9081 var sz = Roo.getGridSize();
9085 var cols = []; // visable cols.
9087 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9088 var w = cm.getColumnWidth(i, false);
9090 cols.push( { rel : false, abs : 0 });
9094 cols.push( { rel : false, abs : w });
9096 last = i; // not really..
9099 var w = cm.getColumnWidth(i, sz);
9104 cols.push( { rel : w, abs : false });
9107 var avail = this.bodyEl.dom.clientWidth - total_abs;
9109 var unitWidth = Math.floor(avail / total);
9110 var rem = avail - (unitWidth * total);
9112 var hidden, width, pos = 0 , splithide , left;
9113 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9115 hidden = 'display:none;';
9117 width = 'width:0px;';
9119 if(!cm.isHidden(i)){
9123 // we can honour xs/sm/md/xl ?
9124 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9126 hidden = 'display:none;';
9128 // width should return a small number...
9130 w+=rem; // add the remaining with..
9133 left = "left:" + (pos -4) + "px;";
9134 width = "width:" + w+ "px;";
9137 if (this.responsive) {
9140 hidden = cm.isHidden(i) ? 'display:none;' : '';
9141 splithide = 'display: none;';
9144 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9147 splithide = 'display:none;';
9150 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9151 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9156 //Roo.log(styles.join(''));
9157 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9163 onContextMenu : function(e, t)
9165 this.processEvent("contextmenu", e);
9168 processEvent : function(name, e)
9170 if (name != 'touchstart' ) {
9171 this.fireEvent(name, e);
9174 var t = e.getTarget();
9176 var cell = Roo.get(t);
9182 if(cell.findParent('tfoot', false, true)){
9186 if(cell.findParent('thead', false, true)){
9188 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9189 cell = Roo.get(t).findParent('th', false, true);
9191 Roo.log("failed to find th in thead?");
9192 Roo.log(e.getTarget());
9197 var cellIndex = cell.dom.cellIndex;
9199 var ename = name == 'touchstart' ? 'click' : name;
9200 this.fireEvent("header" + ename, this, cellIndex, e);
9205 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9206 cell = Roo.get(t).findParent('td', false, true);
9208 Roo.log("failed to find th in tbody?");
9209 Roo.log(e.getTarget());
9214 var row = cell.findParent('tr', false, true);
9215 var cellIndex = cell.dom.cellIndex;
9216 var rowIndex = row.dom.rowIndex - 1;
9220 this.fireEvent("row" + name, this, rowIndex, e);
9224 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9230 onMouseover : function(e, el)
9232 var cell = Roo.get(el);
9238 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9239 cell = cell.findParent('td', false, true);
9242 var row = cell.findParent('tr', false, true);
9243 var cellIndex = cell.dom.cellIndex;
9244 var rowIndex = row.dom.rowIndex - 1; // start from 0
9246 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9250 onMouseout : function(e, el)
9252 var cell = Roo.get(el);
9258 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9259 cell = cell.findParent('td', false, true);
9262 var row = cell.findParent('tr', false, true);
9263 var cellIndex = cell.dom.cellIndex;
9264 var rowIndex = row.dom.rowIndex - 1; // start from 0
9266 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9270 onClick : function(e, el)
9272 var cell = Roo.get(el);
9274 if(!cell || (!this.cellSelection && !this.rowSelection)){
9278 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9279 cell = cell.findParent('td', false, true);
9282 if(!cell || typeof(cell) == 'undefined'){
9286 var row = cell.findParent('tr', false, true);
9288 if(!row || typeof(row) == 'undefined'){
9292 var cellIndex = cell.dom.cellIndex;
9293 var rowIndex = this.getRowIndex(row);
9295 // why??? - should these not be based on SelectionModel?
9296 //if(this.cellSelection){
9297 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9300 //if(this.rowSelection){
9301 this.fireEvent('rowclick', this, row, rowIndex, e);
9306 onDblClick : function(e,el)
9308 var cell = Roo.get(el);
9310 if(!cell || (!this.cellSelection && !this.rowSelection)){
9314 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9315 cell = cell.findParent('td', false, true);
9318 if(!cell || typeof(cell) == 'undefined'){
9322 var row = cell.findParent('tr', false, true);
9324 if(!row || typeof(row) == 'undefined'){
9328 var cellIndex = cell.dom.cellIndex;
9329 var rowIndex = this.getRowIndex(row);
9331 if(this.cellSelection){
9332 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9335 if(this.rowSelection){
9336 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9339 findRowIndex : function(el)
9341 var cell = Roo.get(el);
9345 var row = cell.findParent('tr', false, true);
9347 if(!row || typeof(row) == 'undefined'){
9350 return this.getRowIndex(row);
9352 sort : function(e,el)
9354 var col = Roo.get(el);
9356 if(!col.hasClass('sortable')){
9360 var sort = col.attr('sort');
9363 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9367 this.store.sortInfo = {field : sort, direction : dir};
9370 Roo.log("calling footer first");
9371 this.footer.onClick('first');
9374 this.store.load({ params : { start : 0 } });
9378 renderHeader : function()
9386 this.totalWidth = 0;
9388 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9390 var config = cm.config[i];
9394 cls : 'x-hcol-' + i,
9397 html: cm.getColumnHeader(i)
9400 var tooltip = cm.getColumnTooltip(i);
9402 c.tooltip = tooltip;
9408 if(typeof(config.sortable) != 'undefined' && config.sortable){
9409 c.cls += ' sortable';
9410 c.html = '<i class="fa"></i>' + c.html;
9413 // could use BS4 hidden-..-down
9415 if(typeof(config.lgHeader) != 'undefined'){
9416 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9419 if(typeof(config.mdHeader) != 'undefined'){
9420 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9423 if(typeof(config.smHeader) != 'undefined'){
9424 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9427 if(typeof(config.xsHeader) != 'undefined'){
9428 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9435 if(typeof(config.tooltip) != 'undefined'){
9436 c.tooltip = config.tooltip;
9439 if(typeof(config.colspan) != 'undefined'){
9440 c.colspan = config.colspan;
9443 // hidden is handled by CSS now
9445 if(typeof(config.dataIndex) != 'undefined'){
9446 c.sort = config.dataIndex;
9451 if(typeof(config.align) != 'undefined' && config.align.length){
9452 c.style += ' text-align:' + config.align + ';';
9455 /* width is done in CSS
9456 *if(typeof(config.width) != 'undefined'){
9457 c.style += ' width:' + config.width + 'px;';
9458 this.totalWidth += config.width;
9460 this.totalWidth += 100; // assume minimum of 100 per column?
9464 if(typeof(config.cls) != 'undefined'){
9465 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9467 // this is the bit that doesnt reall work at all...
9469 if (this.responsive) {
9472 ['xs','sm','md','lg'].map(function(size){
9474 if(typeof(config[size]) == 'undefined'){
9478 if (!config[size]) { // 0 = hidden
9479 // BS 4 '0' is treated as hide that column and below.
9480 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9484 c.cls += ' col-' + size + '-' + config[size] + (
9485 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9493 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9504 renderBody : function()
9514 colspan : this.cm.getColumnCount()
9524 renderFooter : function()
9534 colspan : this.cm.getColumnCount()
9548 // Roo.log('ds onload');
9553 var ds = this.store;
9555 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9556 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9557 if (_this.store.sortInfo) {
9559 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9560 e.select('i', true).addClass(['fa-arrow-up']);
9563 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9564 e.select('i', true).addClass(['fa-arrow-down']);
9569 var tbody = this.bodyEl;
9571 if(ds.getCount() > 0){
9572 ds.data.each(function(d,rowIndex){
9573 var row = this.renderRow(cm, ds, rowIndex);
9575 tbody.createChild(row);
9579 if(row.cellObjects.length){
9580 Roo.each(row.cellObjects, function(r){
9581 _this.renderCellObject(r);
9588 var tfoot = this.el.select('tfoot', true).first();
9590 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9592 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9594 var total = this.ds.getTotalCount();
9596 if(this.footer.pageSize < total){
9597 this.mainFoot.show();
9601 Roo.each(this.el.select('tbody td', true).elements, function(e){
9602 e.on('mouseover', _this.onMouseover, _this);
9605 Roo.each(this.el.select('tbody td', true).elements, function(e){
9606 e.on('mouseout', _this.onMouseout, _this);
9608 this.fireEvent('rowsrendered', this);
9612 this.initCSS(); /// resize cols
9618 onUpdate : function(ds,record)
9620 this.refreshRow(record);
9624 onRemove : function(ds, record, index, isUpdate){
9625 if(isUpdate !== true){
9626 this.fireEvent("beforerowremoved", this, index, record);
9628 var bt = this.bodyEl.dom;
9630 var rows = this.el.select('tbody > tr', true).elements;
9632 if(typeof(rows[index]) != 'undefined'){
9633 bt.removeChild(rows[index].dom);
9636 // if(bt.rows[index]){
9637 // bt.removeChild(bt.rows[index]);
9640 if(isUpdate !== true){
9641 //this.stripeRows(index);
9642 //this.syncRowHeights(index, index);
9644 this.fireEvent("rowremoved", this, index, record);
9648 onAdd : function(ds, records, rowIndex)
9650 //Roo.log('on Add called');
9651 // - note this does not handle multiple adding very well..
9652 var bt = this.bodyEl.dom;
9653 for (var i =0 ; i < records.length;i++) {
9654 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9655 //Roo.log(records[i]);
9656 //Roo.log(this.store.getAt(rowIndex+i));
9657 this.insertRow(this.store, rowIndex + i, false);
9664 refreshRow : function(record){
9665 var ds = this.store, index;
9666 if(typeof record == 'number'){
9668 record = ds.getAt(index);
9670 index = ds.indexOf(record);
9672 return; // should not happen - but seems to
9675 this.insertRow(ds, index, true);
9677 this.onRemove(ds, record, index+1, true);
9679 //this.syncRowHeights(index, index);
9681 this.fireEvent("rowupdated", this, index, record);
9683 // private - called by RowSelection
9684 onRowSelect : function(rowIndex){
9685 var row = this.getRowDom(rowIndex);
9686 row.addClass(['bg-info','info']);
9688 // private - called by RowSelection
9689 onRowDeselect : function(rowIndex)
9694 var row = this.getRowDom(rowIndex);
9695 row.removeClass(['bg-info','info']);
9698 * Focuses the specified row.
9699 * @param {Number} row The row index
9701 focusRow : function(row)
9703 //Roo.log('GridView.focusRow');
9704 var x = this.bodyEl.dom.scrollLeft;
9705 this.focusCell(row, 0, false);
9706 this.bodyEl.dom.scrollLeft = x;
9710 * Focuses the specified cell.
9711 * @param {Number} row The row index
9712 * @param {Number} col The column index
9713 * @param {Boolean} hscroll false to disable horizontal scrolling
9715 focusCell : function(row, col, hscroll)
9717 //Roo.log('GridView.focusCell');
9718 var el = this.ensureVisible(row, col, hscroll);
9719 // not sure what focusEL achives = it's a <a> pos relative
9720 //this.focusEl.alignTo(el, "tl-tl");
9722 // this.focusEl.focus();
9724 // this.focusEl.focus.defer(1, this.focusEl);
9729 * Scrolls the specified cell into view
9730 * @param {Number} row The row index
9731 * @param {Number} col The column index
9732 * @param {Boolean} hscroll false to disable horizontal scrolling
9734 ensureVisible : function(row, col, hscroll)
9736 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9737 //return null; //disable for testing.
9738 if(typeof row != "number"){
9741 if(row < 0 && row >= this.ds.getCount()){
9744 col = (col !== undefined ? col : 0);
9746 while(cm.isHidden(col)){
9750 var el = this.getCellDom(row, col);
9754 var c = this.bodyEl.dom;
9756 var ctop = parseInt(el.offsetTop, 10);
9757 var cleft = parseInt(el.offsetLeft, 10);
9758 var cbot = ctop + el.offsetHeight;
9759 var cright = cleft + el.offsetWidth;
9761 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9762 var ch = 0; //?? header is not withing the area?
9763 var stop = parseInt(c.scrollTop, 10);
9764 var sleft = parseInt(c.scrollLeft, 10);
9765 var sbot = stop + ch;
9766 var sright = sleft + c.clientWidth;
9768 Roo.log('GridView.ensureVisible:' +
9770 ' c.clientHeight:' + c.clientHeight +
9771 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9780 //Roo.log("set scrolltop to ctop DISABLE?");
9781 }else if(cbot > sbot){
9782 //Roo.log("set scrolltop to cbot-ch");
9783 c.scrollTop = cbot-ch;
9786 if(hscroll !== false){
9788 c.scrollLeft = cleft;
9789 }else if(cright > sright){
9790 c.scrollLeft = cright-c.clientWidth;
9798 insertRow : function(dm, rowIndex, isUpdate){
9801 this.fireEvent("beforerowsinserted", this, rowIndex);
9803 //var s = this.getScrollState();
9804 var row = this.renderRow(this.cm, this.store, rowIndex);
9805 // insert before rowIndex..
9806 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9810 if(row.cellObjects.length){
9811 Roo.each(row.cellObjects, function(r){
9812 _this.renderCellObject(r);
9817 this.fireEvent("rowsinserted", this, rowIndex);
9818 //this.syncRowHeights(firstRow, lastRow);
9819 //this.stripeRows(firstRow);
9826 getRowDom : function(rowIndex)
9828 var rows = this.el.select('tbody > tr', true).elements;
9830 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9833 getCellDom : function(rowIndex, colIndex)
9835 var row = this.getRowDom(rowIndex);
9836 if (row === false) {
9839 var cols = row.select('td', true).elements;
9840 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9844 // returns the object tree for a tr..
9847 renderRow : function(cm, ds, rowIndex)
9849 var d = ds.getAt(rowIndex);
9853 cls : 'x-row-' + rowIndex,
9857 var cellObjects = [];
9859 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9860 var config = cm.config[i];
9862 var renderer = cm.getRenderer(i);
9866 if(typeof(renderer) !== 'undefined'){
9867 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9869 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9870 // and are rendered into the cells after the row is rendered - using the id for the element.
9872 if(typeof(value) === 'object'){
9882 rowIndex : rowIndex,
9887 this.fireEvent('rowclass', this, rowcfg);
9891 // this might end up displaying HTML?
9892 // this is too messy... - better to only do it on columsn you know are going to be too long
9893 //tooltip : (typeof(value) === 'object') ? '' : value,
9894 cls : rowcfg.rowClass + ' x-col-' + i,
9896 html: (typeof(value) === 'object') ? '' : value
9903 if(typeof(config.colspan) != 'undefined'){
9904 td.colspan = config.colspan;
9909 if(typeof(config.align) != 'undefined' && config.align.length){
9910 td.style += ' text-align:' + config.align + ';';
9912 if(typeof(config.valign) != 'undefined' && config.valign.length){
9913 td.style += ' vertical-align:' + config.valign + ';';
9916 if(typeof(config.width) != 'undefined'){
9917 td.style += ' width:' + config.width + 'px;';
9921 if(typeof(config.cursor) != 'undefined'){
9922 td.style += ' cursor:' + config.cursor + ';';
9925 if(typeof(config.cls) != 'undefined'){
9926 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9928 if (this.responsive) {
9929 ['xs','sm','md','lg'].map(function(size){
9931 if(typeof(config[size]) == 'undefined'){
9937 if (!config[size]) { // 0 = hidden
9938 // BS 4 '0' is treated as hide that column and below.
9939 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9943 td.cls += ' col-' + size + '-' + config[size] + (
9944 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9954 row.cellObjects = cellObjects;
9962 onBeforeLoad : function()
9971 this.el.select('tbody', true).first().dom.innerHTML = '';
9974 * Show or hide a row.
9975 * @param {Number} rowIndex to show or hide
9976 * @param {Boolean} state hide
9978 setRowVisibility : function(rowIndex, state)
9980 var bt = this.bodyEl.dom;
9982 var rows = this.el.select('tbody > tr', true).elements;
9984 if(typeof(rows[rowIndex]) == 'undefined'){
9987 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9992 getSelectionModel : function(){
9994 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9996 return this.selModel;
9999 * Render the Roo.bootstrap object from renderder
10001 renderCellObject : function(r)
10005 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10007 var t = r.cfg.render(r.container);
10010 Roo.each(r.cfg.cn, function(c){
10012 container: t.getChildContainer(),
10015 _this.renderCellObject(child);
10020 * get the Row Index from a dom element.
10021 * @param {Roo.Element} row The row to look for
10022 * @returns {Number} the row
10024 getRowIndex : function(row)
10028 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10039 * get the header TH element for columnIndex
10040 * @param {Number} columnIndex
10041 * @returns {Roo.Element}
10043 getHeaderIndex: function(colIndex)
10045 var cols = this.headEl.select('th', true).elements;
10046 return cols[colIndex];
10049 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10050 * @param {domElement} cell to look for
10051 * @returns {Number} the column
10053 getCellIndex : function(cell)
10055 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10057 return parseInt(id[1], 10);
10062 * Returns the grid's underlying element = used by panel.Grid
10063 * @return {Element} The element
10065 getGridEl : function(){
10069 * Forces a resize - used by panel.Grid
10070 * @return {Element} The element
10072 autoSize : function()
10074 //var ctr = Roo.get(this.container.dom.parentElement);
10075 var ctr = Roo.get(this.el.dom);
10077 var thd = this.getGridEl().select('thead',true).first();
10078 var tbd = this.getGridEl().select('tbody', true).first();
10079 var tfd = this.getGridEl().select('tfoot', true).first();
10081 var cw = ctr.getWidth();
10082 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10086 tbd.setWidth(ctr.getWidth());
10087 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10088 // this needs fixing for various usage - currently only hydra job advers I think..
10090 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10092 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10095 cw = Math.max(cw, this.totalWidth);
10096 this.getGridEl().select('tbody tr',true).setWidth(cw);
10099 // resize 'expandable coloumn?
10101 return; // we doe not have a view in this design..
10104 onBodyScroll: function()
10106 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10108 this.headEl.setStyle({
10109 'position' : 'relative',
10110 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10116 var scrollHeight = this.bodyEl.dom.scrollHeight;
10118 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10120 var height = this.bodyEl.getHeight();
10122 if(scrollHeight - height == scrollTop) {
10124 var total = this.ds.getTotalCount();
10126 if(this.footer.cursor + this.footer.pageSize < total){
10128 this.footer.ds.load({
10130 start : this.footer.cursor + this.footer.pageSize,
10131 limit : this.footer.pageSize
10140 onColumnSplitterMoved : function(i, diff)
10142 this.userResized = true;
10144 var cm = this.colModel;
10146 var w = this.getHeaderIndex(i).getWidth() + diff;
10149 cm.setColumnWidth(i, w, true);
10151 //var cid = cm.getColumnId(i); << not used in this version?
10152 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10154 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10155 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10156 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10158 //this.updateSplitters();
10159 //this.layout(); << ??
10160 this.fireEvent("columnresize", i, w);
10162 onHeaderChange : function()
10164 var header = this.renderHeader();
10165 var table = this.el.select('table', true).first();
10167 this.headEl.remove();
10168 this.headEl = table.createChild(header, this.bodyEl, false);
10170 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10171 e.on('click', this.sort, this);
10174 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10175 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10180 onHiddenChange : function(colModel, colIndex, hidden)
10183 this.cm.setHidden()
10184 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10185 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10187 this.CSS.updateRule(thSelector, "display", "");
10188 this.CSS.updateRule(tdSelector, "display", "");
10191 this.CSS.updateRule(thSelector, "display", "none");
10192 this.CSS.updateRule(tdSelector, "display", "none");
10195 // onload calls initCSS()
10196 this.onHeaderChange();
10200 setColumnWidth: function(col_index, width)
10202 // width = "md-2 xs-2..."
10203 if(!this.colModel.config[col_index]) {
10207 var w = width.split(" ");
10209 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10211 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10214 for(var j = 0; j < w.length; j++) {
10220 var size_cls = w[j].split("-");
10222 if(!Number.isInteger(size_cls[1] * 1)) {
10226 if(!this.colModel.config[col_index][size_cls[0]]) {
10230 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10234 h_row[0].classList.replace(
10235 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10236 "col-"+size_cls[0]+"-"+size_cls[1]
10239 for(var i = 0; i < rows.length; i++) {
10241 var size_cls = w[j].split("-");
10243 if(!Number.isInteger(size_cls[1] * 1)) {
10247 if(!this.colModel.config[col_index][size_cls[0]]) {
10251 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10255 rows[i].classList.replace(
10256 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10257 "col-"+size_cls[0]+"-"+size_cls[1]
10261 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10266 // currently only used to find the split on drag..
10267 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10272 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10273 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10282 * @class Roo.bootstrap.TableCell
10283 * @extends Roo.bootstrap.Component
10284 * Bootstrap TableCell class
10285 * @cfg {String} html cell contain text
10286 * @cfg {String} cls cell class
10287 * @cfg {String} tag cell tag (td|th) default td
10288 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10289 * @cfg {String} align Aligns the content in a cell
10290 * @cfg {String} axis Categorizes cells
10291 * @cfg {String} bgcolor Specifies the background color of a cell
10292 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10293 * @cfg {Number} colspan Specifies the number of columns a cell should span
10294 * @cfg {String} headers Specifies one or more header cells a cell is related to
10295 * @cfg {Number} height Sets the height of a cell
10296 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10297 * @cfg {Number} rowspan Sets the number of rows a cell should span
10298 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10299 * @cfg {String} valign Vertical aligns the content in a cell
10300 * @cfg {Number} width Specifies the width of a cell
10303 * Create a new TableCell
10304 * @param {Object} config The config object
10307 Roo.bootstrap.TableCell = function(config){
10308 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10311 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10331 getAutoCreate : function(){
10332 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10339 cfg.tag = this.tag;
10352 cfg.align=this.align
10357 if (this.bgcolor) {
10358 cfg.bgcolor=this.bgcolor
10360 if (this.charoff) {
10361 cfg.charoff=this.charoff
10363 if (this.colspan) {
10364 cfg.colspan=this.colspan
10366 if (this.headers) {
10367 cfg.headers=this.headers
10370 cfg.height=this.height
10373 cfg.nowrap=this.nowrap
10375 if (this.rowspan) {
10376 cfg.rowspan=this.rowspan
10379 cfg.scope=this.scope
10382 cfg.valign=this.valign
10385 cfg.width=this.width
10404 * @class Roo.bootstrap.TableRow
10405 * @extends Roo.bootstrap.Component
10406 * Bootstrap TableRow class
10407 * @cfg {String} cls row class
10408 * @cfg {String} align Aligns the content in a table row
10409 * @cfg {String} bgcolor Specifies a background color for a table row
10410 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10411 * @cfg {String} valign Vertical aligns the content in a table row
10414 * Create a new TableRow
10415 * @param {Object} config The config object
10418 Roo.bootstrap.TableRow = function(config){
10419 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10422 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10430 getAutoCreate : function(){
10431 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10438 cfg.cls = this.cls;
10441 cfg.align = this.align;
10444 cfg.bgcolor = this.bgcolor;
10447 cfg.charoff = this.charoff;
10450 cfg.valign = this.valign;
10468 * @class Roo.bootstrap.TableBody
10469 * @extends Roo.bootstrap.Component
10470 * Bootstrap TableBody class
10471 * @cfg {String} cls element class
10472 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10473 * @cfg {String} align Aligns the content inside the element
10474 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10475 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10478 * Create a new TableBody
10479 * @param {Object} config The config object
10482 Roo.bootstrap.TableBody = function(config){
10483 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10486 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10494 getAutoCreate : function(){
10495 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10505 cfg.tag = this.tag;
10509 cfg.align = this.align;
10512 cfg.charoff = this.charoff;
10515 cfg.valign = this.valign;
10522 // initEvents : function()
10525 // if(!this.store){
10529 // this.store = Roo.factory(this.store, Roo.data);
10530 // this.store.on('load', this.onLoad, this);
10532 // this.store.load();
10536 // onLoad: function ()
10538 // this.fireEvent('load', this);
10548 * Ext JS Library 1.1.1
10549 * Copyright(c) 2006-2007, Ext JS, LLC.
10551 * Originally Released Under LGPL - original licence link has changed is not relivant.
10554 * <script type="text/javascript">
10557 // as we use this in bootstrap.
10558 Roo.namespace('Roo.form');
10560 * @class Roo.form.Action
10561 * Internal Class used to handle form actions
10563 * @param {Roo.form.BasicForm} el The form element or its id
10564 * @param {Object} config Configuration options
10569 // define the action interface
10570 Roo.form.Action = function(form, options){
10572 this.options = options || {};
10575 * Client Validation Failed
10578 Roo.form.Action.CLIENT_INVALID = 'client';
10580 * Server Validation Failed
10583 Roo.form.Action.SERVER_INVALID = 'server';
10585 * Connect to Server Failed
10588 Roo.form.Action.CONNECT_FAILURE = 'connect';
10590 * Reading Data from Server Failed
10593 Roo.form.Action.LOAD_FAILURE = 'load';
10595 Roo.form.Action.prototype = {
10597 failureType : undefined,
10598 response : undefined,
10599 result : undefined,
10601 // interface method
10602 run : function(options){
10606 // interface method
10607 success : function(response){
10611 // interface method
10612 handleResponse : function(response){
10616 // default connection failure
10617 failure : function(response){
10619 this.response = response;
10620 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10621 this.form.afterAction(this, false);
10624 processResponse : function(response){
10625 this.response = response;
10626 if(!response.responseText){
10629 this.result = this.handleResponse(response);
10630 return this.result;
10633 // utility functions used internally
10634 getUrl : function(appendParams){
10635 var url = this.options.url || this.form.url || this.form.el.dom.action;
10637 var p = this.getParams();
10639 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10645 getMethod : function(){
10646 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10649 getParams : function(){
10650 var bp = this.form.baseParams;
10651 var p = this.options.params;
10653 if(typeof p == "object"){
10654 p = Roo.urlEncode(Roo.applyIf(p, bp));
10655 }else if(typeof p == 'string' && bp){
10656 p += '&' + Roo.urlEncode(bp);
10659 p = Roo.urlEncode(bp);
10664 createCallback : function(){
10666 success: this.success,
10667 failure: this.failure,
10669 timeout: (this.form.timeout*1000),
10670 upload: this.form.fileUpload ? this.success : undefined
10675 Roo.form.Action.Submit = function(form, options){
10676 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10679 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10682 haveProgress : false,
10683 uploadComplete : false,
10685 // uploadProgress indicator.
10686 uploadProgress : function()
10688 if (!this.form.progressUrl) {
10692 if (!this.haveProgress) {
10693 Roo.MessageBox.progress("Uploading", "Uploading");
10695 if (this.uploadComplete) {
10696 Roo.MessageBox.hide();
10700 this.haveProgress = true;
10702 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10704 var c = new Roo.data.Connection();
10706 url : this.form.progressUrl,
10711 success : function(req){
10712 //console.log(data);
10716 rdata = Roo.decode(req.responseText)
10718 Roo.log("Invalid data from server..");
10722 if (!rdata || !rdata.success) {
10724 Roo.MessageBox.alert(Roo.encode(rdata));
10727 var data = rdata.data;
10729 if (this.uploadComplete) {
10730 Roo.MessageBox.hide();
10735 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10736 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10739 this.uploadProgress.defer(2000,this);
10742 failure: function(data) {
10743 Roo.log('progress url failed ');
10754 // run get Values on the form, so it syncs any secondary forms.
10755 this.form.getValues();
10757 var o = this.options;
10758 var method = this.getMethod();
10759 var isPost = method == 'POST';
10760 if(o.clientValidation === false || this.form.isValid()){
10762 if (this.form.progressUrl) {
10763 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10764 (new Date() * 1) + '' + Math.random());
10769 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10770 form:this.form.el.dom,
10771 url:this.getUrl(!isPost),
10773 params:isPost ? this.getParams() : null,
10774 isUpload: this.form.fileUpload,
10775 formData : this.form.formData
10778 this.uploadProgress();
10780 }else if (o.clientValidation !== false){ // client validation failed
10781 this.failureType = Roo.form.Action.CLIENT_INVALID;
10782 this.form.afterAction(this, false);
10786 success : function(response)
10788 this.uploadComplete= true;
10789 if (this.haveProgress) {
10790 Roo.MessageBox.hide();
10794 var result = this.processResponse(response);
10795 if(result === true || result.success){
10796 this.form.afterAction(this, true);
10800 this.form.markInvalid(result.errors);
10801 this.failureType = Roo.form.Action.SERVER_INVALID;
10803 this.form.afterAction(this, false);
10805 failure : function(response)
10807 this.uploadComplete= true;
10808 if (this.haveProgress) {
10809 Roo.MessageBox.hide();
10812 this.response = response;
10813 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10814 this.form.afterAction(this, false);
10817 handleResponse : function(response){
10818 if(this.form.errorReader){
10819 var rs = this.form.errorReader.read(response);
10822 for(var i = 0, len = rs.records.length; i < len; i++) {
10823 var r = rs.records[i];
10824 errors[i] = r.data;
10827 if(errors.length < 1){
10831 success : rs.success,
10837 ret = Roo.decode(response.responseText);
10841 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10851 Roo.form.Action.Load = function(form, options){
10852 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10853 this.reader = this.form.reader;
10856 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10861 Roo.Ajax.request(Roo.apply(
10862 this.createCallback(), {
10863 method:this.getMethod(),
10864 url:this.getUrl(false),
10865 params:this.getParams()
10869 success : function(response){
10871 var result = this.processResponse(response);
10872 if(result === true || !result.success || !result.data){
10873 this.failureType = Roo.form.Action.LOAD_FAILURE;
10874 this.form.afterAction(this, false);
10877 this.form.clearInvalid();
10878 this.form.setValues(result.data);
10879 this.form.afterAction(this, true);
10882 handleResponse : function(response){
10883 if(this.form.reader){
10884 var rs = this.form.reader.read(response);
10885 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10887 success : rs.success,
10891 return Roo.decode(response.responseText);
10895 Roo.form.Action.ACTION_TYPES = {
10896 'load' : Roo.form.Action.Load,
10897 'submit' : Roo.form.Action.Submit
10906 * @class Roo.bootstrap.Form
10907 * @extends Roo.bootstrap.Component
10908 * Bootstrap Form class
10909 * @cfg {String} method GET | POST (default POST)
10910 * @cfg {String} labelAlign top | left (default top)
10911 * @cfg {String} align left | right - for navbars
10912 * @cfg {Boolean} loadMask load mask when submit (default true)
10916 * Create a new Form
10917 * @param {Object} config The config object
10921 Roo.bootstrap.Form = function(config){
10923 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10925 Roo.bootstrap.Form.popover.apply();
10929 * @event clientvalidation
10930 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10931 * @param {Form} this
10932 * @param {Boolean} valid true if the form has passed client-side validation
10934 clientvalidation: true,
10936 * @event beforeaction
10937 * Fires before any action is performed. Return false to cancel the action.
10938 * @param {Form} this
10939 * @param {Action} action The action to be performed
10941 beforeaction: true,
10943 * @event actionfailed
10944 * Fires when an action fails.
10945 * @param {Form} this
10946 * @param {Action} action The action that failed
10948 actionfailed : true,
10950 * @event actioncomplete
10951 * Fires when an action is completed.
10952 * @param {Form} this
10953 * @param {Action} action The action that completed
10955 actioncomplete : true
10959 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10962 * @cfg {String} method
10963 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10967 * @cfg {String} url
10968 * The URL to use for form actions if one isn't supplied in the action options.
10971 * @cfg {Boolean} fileUpload
10972 * Set to true if this form is a file upload.
10976 * @cfg {Object} baseParams
10977 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10981 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10985 * @cfg {Sting} align (left|right) for navbar forms
10990 activeAction : null,
10993 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10994 * element by passing it or its id or mask the form itself by passing in true.
10997 waitMsgTarget : false,
11002 * @cfg {Boolean} errorMask (true|false) default false
11007 * @cfg {Number} maskOffset Default 100
11012 * @cfg {Boolean} maskBody
11016 getAutoCreate : function(){
11020 method : this.method || 'POST',
11021 id : this.id || Roo.id(),
11024 if (this.parent().xtype.match(/^Nav/)) {
11025 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11029 if (this.labelAlign == 'left' ) {
11030 cfg.cls += ' form-horizontal';
11036 initEvents : function()
11038 this.el.on('submit', this.onSubmit, this);
11039 // this was added as random key presses on the form where triggering form submit.
11040 this.el.on('keypress', function(e) {
11041 if (e.getCharCode() != 13) {
11044 // we might need to allow it for textareas.. and some other items.
11045 // check e.getTarget().
11047 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11051 Roo.log("keypress blocked");
11053 e.preventDefault();
11059 onSubmit : function(e){
11064 * Returns true if client-side validation on the form is successful.
11067 isValid : function(){
11068 var items = this.getItems();
11070 var target = false;
11072 items.each(function(f){
11078 Roo.log('invalid field: ' + f.name);
11082 if(!target && f.el.isVisible(true)){
11088 if(this.errorMask && !valid){
11089 Roo.bootstrap.Form.popover.mask(this, target);
11096 * Returns true if any fields in this form have changed since their original load.
11099 isDirty : function(){
11101 var items = this.getItems();
11102 items.each(function(f){
11112 * Performs a predefined action (submit or load) or custom actions you define on this form.
11113 * @param {String} actionName The name of the action type
11114 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11115 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11116 * accept other config options):
11118 Property Type Description
11119 ---------------- --------------- ----------------------------------------------------------------------------------
11120 url String The url for the action (defaults to the form's url)
11121 method String The form method to use (defaults to the form's method, or POST if not defined)
11122 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11123 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11124 validate the form on the client (defaults to false)
11126 * @return {BasicForm} this
11128 doAction : function(action, options){
11129 if(typeof action == 'string'){
11130 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11132 if(this.fireEvent('beforeaction', this, action) !== false){
11133 this.beforeAction(action);
11134 action.run.defer(100, action);
11140 beforeAction : function(action){
11141 var o = action.options;
11146 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11148 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11151 // not really supported yet.. ??
11153 //if(this.waitMsgTarget === true){
11154 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11155 //}else if(this.waitMsgTarget){
11156 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11157 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11159 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11165 afterAction : function(action, success){
11166 this.activeAction = null;
11167 var o = action.options;
11172 Roo.get(document.body).unmask();
11178 //if(this.waitMsgTarget === true){
11179 // this.el.unmask();
11180 //}else if(this.waitMsgTarget){
11181 // this.waitMsgTarget.unmask();
11183 // Roo.MessageBox.updateProgress(1);
11184 // Roo.MessageBox.hide();
11191 Roo.callback(o.success, o.scope, [this, action]);
11192 this.fireEvent('actioncomplete', this, action);
11196 // failure condition..
11197 // we have a scenario where updates need confirming.
11198 // eg. if a locking scenario exists..
11199 // we look for { errors : { needs_confirm : true }} in the response.
11201 (typeof(action.result) != 'undefined') &&
11202 (typeof(action.result.errors) != 'undefined') &&
11203 (typeof(action.result.errors.needs_confirm) != 'undefined')
11206 Roo.log("not supported yet");
11209 Roo.MessageBox.confirm(
11210 "Change requires confirmation",
11211 action.result.errorMsg,
11216 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11226 Roo.callback(o.failure, o.scope, [this, action]);
11227 // show an error message if no failed handler is set..
11228 if (!this.hasListener('actionfailed')) {
11229 Roo.log("need to add dialog support");
11231 Roo.MessageBox.alert("Error",
11232 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11233 action.result.errorMsg :
11234 "Saving Failed, please check your entries or try again"
11239 this.fireEvent('actionfailed', this, action);
11244 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11245 * @param {String} id The value to search for
11248 findField : function(id){
11249 var items = this.getItems();
11250 var field = items.get(id);
11252 items.each(function(f){
11253 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11260 return field || null;
11263 * Mark fields in this form invalid in bulk.
11264 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11265 * @return {BasicForm} this
11267 markInvalid : function(errors){
11268 if(errors instanceof Array){
11269 for(var i = 0, len = errors.length; i < len; i++){
11270 var fieldError = errors[i];
11271 var f = this.findField(fieldError.id);
11273 f.markInvalid(fieldError.msg);
11279 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11280 field.markInvalid(errors[id]);
11284 //Roo.each(this.childForms || [], function (f) {
11285 // f.markInvalid(errors);
11292 * Set values for fields in this form in bulk.
11293 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11294 * @return {BasicForm} this
11296 setValues : function(values){
11297 if(values instanceof Array){ // array of objects
11298 for(var i = 0, len = values.length; i < len; i++){
11300 var f = this.findField(v.id);
11302 f.setValue(v.value);
11303 if(this.trackResetOnLoad){
11304 f.originalValue = f.getValue();
11308 }else{ // object hash
11311 if(typeof values[id] != 'function' && (field = this.findField(id))){
11313 if (field.setFromData &&
11314 field.valueField &&
11315 field.displayField &&
11316 // combos' with local stores can
11317 // be queried via setValue()
11318 // to set their value..
11319 (field.store && !field.store.isLocal)
11323 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11324 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11325 field.setFromData(sd);
11327 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11329 field.setFromData(values);
11332 field.setValue(values[id]);
11336 if(this.trackResetOnLoad){
11337 field.originalValue = field.getValue();
11343 //Roo.each(this.childForms || [], function (f) {
11344 // f.setValues(values);
11351 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11352 * they are returned as an array.
11353 * @param {Boolean} asString
11356 getValues : function(asString){
11357 //if (this.childForms) {
11358 // copy values from the child forms
11359 // Roo.each(this.childForms, function (f) {
11360 // this.setValues(f.getValues());
11366 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11367 if(asString === true){
11370 return Roo.urlDecode(fs);
11374 * Returns the fields in this form as an object with key/value pairs.
11375 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11378 getFieldValues : function(with_hidden)
11380 var items = this.getItems();
11382 items.each(function(f){
11384 if (!f.getName()) {
11388 var v = f.getValue();
11390 if (f.inputType =='radio') {
11391 if (typeof(ret[f.getName()]) == 'undefined') {
11392 ret[f.getName()] = ''; // empty..
11395 if (!f.el.dom.checked) {
11399 v = f.el.dom.value;
11403 if(f.xtype == 'MoneyField'){
11404 ret[f.currencyName] = f.getCurrency();
11407 // not sure if this supported any more..
11408 if ((typeof(v) == 'object') && f.getRawValue) {
11409 v = f.getRawValue() ; // dates..
11411 // combo boxes where name != hiddenName...
11412 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11413 ret[f.name] = f.getRawValue();
11415 ret[f.getName()] = v;
11422 * Clears all invalid messages in this form.
11423 * @return {BasicForm} this
11425 clearInvalid : function(){
11426 var items = this.getItems();
11428 items.each(function(f){
11436 * Resets this form.
11437 * @return {BasicForm} this
11439 reset : function(){
11440 var items = this.getItems();
11441 items.each(function(f){
11445 Roo.each(this.childForms || [], function (f) {
11453 getItems : function()
11455 var r=new Roo.util.MixedCollection(false, function(o){
11456 return o.id || (o.id = Roo.id());
11458 var iter = function(el) {
11465 Roo.each(el.items,function(e) {
11474 hideFields : function(items)
11476 Roo.each(items, function(i){
11478 var f = this.findField(i);
11489 showFields : function(items)
11491 Roo.each(items, function(i){
11493 var f = this.findField(i);
11506 Roo.apply(Roo.bootstrap.Form, {
11522 intervalID : false,
11528 if(this.isApplied){
11533 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11534 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11535 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11536 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11539 this.maskEl.top.enableDisplayMode("block");
11540 this.maskEl.left.enableDisplayMode("block");
11541 this.maskEl.bottom.enableDisplayMode("block");
11542 this.maskEl.right.enableDisplayMode("block");
11544 this.toolTip = new Roo.bootstrap.Tooltip({
11545 cls : 'roo-form-error-popover',
11547 'left' : ['r-l', [-2,0], 'right'],
11548 'right' : ['l-r', [2,0], 'left'],
11549 'bottom' : ['tl-bl', [0,2], 'top'],
11550 'top' : [ 'bl-tl', [0,-2], 'bottom']
11554 this.toolTip.render(Roo.get(document.body));
11556 this.toolTip.el.enableDisplayMode("block");
11558 Roo.get(document.body).on('click', function(){
11562 Roo.get(document.body).on('touchstart', function(){
11566 this.isApplied = true
11569 mask : function(form, target)
11573 this.target = target;
11575 if(!this.form.errorMask || !target.el){
11579 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11581 Roo.log(scrollable);
11583 var ot = this.target.el.calcOffsetsTo(scrollable);
11585 var scrollTo = ot[1] - this.form.maskOffset;
11587 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11589 scrollable.scrollTo('top', scrollTo);
11591 var box = this.target.el.getBox();
11593 var zIndex = Roo.bootstrap.Modal.zIndex++;
11596 this.maskEl.top.setStyle('position', 'absolute');
11597 this.maskEl.top.setStyle('z-index', zIndex);
11598 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11599 this.maskEl.top.setLeft(0);
11600 this.maskEl.top.setTop(0);
11601 this.maskEl.top.show();
11603 this.maskEl.left.setStyle('position', 'absolute');
11604 this.maskEl.left.setStyle('z-index', zIndex);
11605 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11606 this.maskEl.left.setLeft(0);
11607 this.maskEl.left.setTop(box.y - this.padding);
11608 this.maskEl.left.show();
11610 this.maskEl.bottom.setStyle('position', 'absolute');
11611 this.maskEl.bottom.setStyle('z-index', zIndex);
11612 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11613 this.maskEl.bottom.setLeft(0);
11614 this.maskEl.bottom.setTop(box.bottom + this.padding);
11615 this.maskEl.bottom.show();
11617 this.maskEl.right.setStyle('position', 'absolute');
11618 this.maskEl.right.setStyle('z-index', zIndex);
11619 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11620 this.maskEl.right.setLeft(box.right + this.padding);
11621 this.maskEl.right.setTop(box.y - this.padding);
11622 this.maskEl.right.show();
11624 this.toolTip.bindEl = this.target.el;
11626 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11628 var tip = this.target.blankText;
11630 if(this.target.getValue() !== '' ) {
11632 if (this.target.invalidText.length) {
11633 tip = this.target.invalidText;
11634 } else if (this.target.regexText.length){
11635 tip = this.target.regexText;
11639 this.toolTip.show(tip);
11641 this.intervalID = window.setInterval(function() {
11642 Roo.bootstrap.Form.popover.unmask();
11645 window.onwheel = function(){ return false;};
11647 (function(){ this.isMasked = true; }).defer(500, this);
11651 unmask : function()
11653 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11657 this.maskEl.top.setStyle('position', 'absolute');
11658 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11659 this.maskEl.top.hide();
11661 this.maskEl.left.setStyle('position', 'absolute');
11662 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11663 this.maskEl.left.hide();
11665 this.maskEl.bottom.setStyle('position', 'absolute');
11666 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11667 this.maskEl.bottom.hide();
11669 this.maskEl.right.setStyle('position', 'absolute');
11670 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11671 this.maskEl.right.hide();
11673 this.toolTip.hide();
11675 this.toolTip.el.hide();
11677 window.onwheel = function(){ return true;};
11679 if(this.intervalID){
11680 window.clearInterval(this.intervalID);
11681 this.intervalID = false;
11684 this.isMasked = false;
11694 * Ext JS Library 1.1.1
11695 * Copyright(c) 2006-2007, Ext JS, LLC.
11697 * Originally Released Under LGPL - original licence link has changed is not relivant.
11700 * <script type="text/javascript">
11703 * @class Roo.form.VTypes
11704 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11707 Roo.form.VTypes = function(){
11708 // closure these in so they are only created once.
11709 var alpha = /^[a-zA-Z_]+$/;
11710 var alphanum = /^[a-zA-Z0-9_]+$/;
11711 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11712 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11714 // All these messages and functions are configurable
11717 * The function used to validate email addresses
11718 * @param {String} value The email address
11720 'email' : function(v){
11721 return email.test(v);
11724 * The error text to display when the email validation function returns false
11727 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11729 * The keystroke filter mask to be applied on email input
11732 'emailMask' : /[a-z0-9_\.\-@]/i,
11735 * The function used to validate URLs
11736 * @param {String} value The URL
11738 'url' : function(v){
11739 return url.test(v);
11742 * The error text to display when the url validation function returns false
11745 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11748 * The function used to validate alpha values
11749 * @param {String} value The value
11751 'alpha' : function(v){
11752 return alpha.test(v);
11755 * The error text to display when the alpha validation function returns false
11758 'alphaText' : 'This field should only contain letters and _',
11760 * The keystroke filter mask to be applied on alpha input
11763 'alphaMask' : /[a-z_]/i,
11766 * The function used to validate alphanumeric values
11767 * @param {String} value The value
11769 'alphanum' : function(v){
11770 return alphanum.test(v);
11773 * The error text to display when the alphanumeric validation function returns false
11776 'alphanumText' : 'This field should only contain letters, numbers and _',
11778 * The keystroke filter mask to be applied on alphanumeric input
11781 'alphanumMask' : /[a-z0-9_]/i
11791 * @class Roo.bootstrap.Input
11792 * @extends Roo.bootstrap.Component
11793 * Bootstrap Input class
11794 * @cfg {Boolean} disabled is it disabled
11795 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11796 * @cfg {String} name name of the input
11797 * @cfg {string} fieldLabel - the label associated
11798 * @cfg {string} placeholder - placeholder to put in text.
11799 * @cfg {string} before - input group add on before
11800 * @cfg {string} after - input group add on after
11801 * @cfg {string} size - (lg|sm) or leave empty..
11802 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11803 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11804 * @cfg {Number} md colspan out of 12 for computer-sized screens
11805 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11806 * @cfg {string} value default value of the input
11807 * @cfg {Number} labelWidth set the width of label
11808 * @cfg {Number} labellg set the width of label (1-12)
11809 * @cfg {Number} labelmd set the width of label (1-12)
11810 * @cfg {Number} labelsm set the width of label (1-12)
11811 * @cfg {Number} labelxs set the width of label (1-12)
11812 * @cfg {String} labelAlign (top|left)
11813 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11814 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11815 * @cfg {String} indicatorpos (left|right) default left
11816 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11817 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11818 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11820 * @cfg {String} align (left|center|right) Default left
11821 * @cfg {Boolean} forceFeedback (true|false) Default false
11824 * Create a new Input
11825 * @param {Object} config The config object
11828 Roo.bootstrap.Input = function(config){
11830 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11835 * Fires when this field receives input focus.
11836 * @param {Roo.form.Field} this
11841 * Fires when this field loses input focus.
11842 * @param {Roo.form.Field} this
11846 * @event specialkey
11847 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11848 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11849 * @param {Roo.form.Field} this
11850 * @param {Roo.EventObject} e The event object
11855 * Fires just before the field blurs if the field value has changed.
11856 * @param {Roo.form.Field} this
11857 * @param {Mixed} newValue The new value
11858 * @param {Mixed} oldValue The original value
11863 * Fires after the field has been marked as invalid.
11864 * @param {Roo.form.Field} this
11865 * @param {String} msg The validation message
11870 * Fires after the field has been validated with no errors.
11871 * @param {Roo.form.Field} this
11876 * Fires after the key up
11877 * @param {Roo.form.Field} this
11878 * @param {Roo.EventObject} e The event Object
11883 * Fires after the user pastes into input
11884 * @param {Roo.form.Field} this
11885 * @param {Roo.EventObject} e The event Object
11891 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11893 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11894 automatic validation (defaults to "keyup").
11896 validationEvent : "keyup",
11898 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11900 validateOnBlur : true,
11902 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11904 validationDelay : 250,
11906 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11908 focusClass : "x-form-focus", // not needed???
11912 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11914 invalidClass : "has-warning",
11917 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11919 validClass : "has-success",
11922 * @cfg {Boolean} hasFeedback (true|false) default true
11924 hasFeedback : true,
11927 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11929 invalidFeedbackClass : "glyphicon-warning-sign",
11932 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11934 validFeedbackClass : "glyphicon-ok",
11937 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11939 selectOnFocus : false,
11942 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11946 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11951 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11953 disableKeyFilter : false,
11956 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11960 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11964 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11966 blankText : "Please complete this mandatory field",
11969 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11973 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11975 maxLength : Number.MAX_VALUE,
11977 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11979 minLengthText : "The minimum length for this field is {0}",
11981 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11983 maxLengthText : "The maximum length for this field is {0}",
11987 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11988 * If available, this function will be called only after the basic validators all return true, and will be passed the
11989 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11993 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11994 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11995 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11999 * @cfg {String} regexText -- Depricated - use Invalid Text
12004 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12010 autocomplete: false,
12014 inputType : 'text',
12017 placeholder: false,
12022 preventMark: false,
12023 isFormField : true,
12026 labelAlign : false,
12029 formatedValue : false,
12030 forceFeedback : false,
12032 indicatorpos : 'left',
12042 parentLabelAlign : function()
12045 while (parent.parent()) {
12046 parent = parent.parent();
12047 if (typeof(parent.labelAlign) !='undefined') {
12048 return parent.labelAlign;
12055 getAutoCreate : function()
12057 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12063 if(this.inputType != 'hidden'){
12064 cfg.cls = 'form-group' //input-group
12070 type : this.inputType,
12071 value : this.value,
12072 cls : 'form-control',
12073 placeholder : this.placeholder || '',
12074 autocomplete : this.autocomplete || 'new-password'
12076 if (this.inputType == 'file') {
12077 input.style = 'overflow:hidden'; // why not in CSS?
12080 if(this.capture.length){
12081 input.capture = this.capture;
12084 if(this.accept.length){
12085 input.accept = this.accept + "/*";
12089 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12092 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12093 input.maxLength = this.maxLength;
12096 if (this.disabled) {
12097 input.disabled=true;
12100 if (this.readOnly) {
12101 input.readonly=true;
12105 input.name = this.name;
12109 input.cls += ' input-' + this.size;
12113 ['xs','sm','md','lg'].map(function(size){
12114 if (settings[size]) {
12115 cfg.cls += ' col-' + size + '-' + settings[size];
12119 var inputblock = input;
12123 cls: 'glyphicon form-control-feedback'
12126 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12129 cls : 'has-feedback',
12137 if (this.before || this.after) {
12140 cls : 'input-group',
12144 if (this.before && typeof(this.before) == 'string') {
12146 inputblock.cn.push({
12148 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12152 if (this.before && typeof(this.before) == 'object') {
12153 this.before = Roo.factory(this.before);
12155 inputblock.cn.push({
12157 cls : 'roo-input-before input-group-prepend input-group-' +
12158 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12162 inputblock.cn.push(input);
12164 if (this.after && typeof(this.after) == 'string') {
12165 inputblock.cn.push({
12167 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12171 if (this.after && typeof(this.after) == 'object') {
12172 this.after = Roo.factory(this.after);
12174 inputblock.cn.push({
12176 cls : 'roo-input-after input-group-append input-group-' +
12177 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12181 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12182 inputblock.cls += ' has-feedback';
12183 inputblock.cn.push(feedback);
12188 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12189 tooltip : 'This field is required'
12191 if (this.allowBlank ) {
12192 indicator.style = this.allowBlank ? ' display:none' : '';
12194 if (align ==='left' && this.fieldLabel.length) {
12196 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12203 cls : 'control-label col-form-label',
12204 html : this.fieldLabel
12215 var labelCfg = cfg.cn[1];
12216 var contentCfg = cfg.cn[2];
12218 if(this.indicatorpos == 'right'){
12223 cls : 'control-label col-form-label',
12227 html : this.fieldLabel
12241 labelCfg = cfg.cn[0];
12242 contentCfg = cfg.cn[1];
12246 if(this.labelWidth > 12){
12247 labelCfg.style = "width: " + this.labelWidth + 'px';
12250 if(this.labelWidth < 13 && this.labelmd == 0){
12251 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12254 if(this.labellg > 0){
12255 labelCfg.cls += ' col-lg-' + this.labellg;
12256 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12259 if(this.labelmd > 0){
12260 labelCfg.cls += ' col-md-' + this.labelmd;
12261 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12264 if(this.labelsm > 0){
12265 labelCfg.cls += ' col-sm-' + this.labelsm;
12266 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12269 if(this.labelxs > 0){
12270 labelCfg.cls += ' col-xs-' + this.labelxs;
12271 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12275 } else if ( this.fieldLabel.length) {
12282 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12283 tooltip : 'This field is required',
12284 style : this.allowBlank ? ' display:none' : ''
12288 //cls : 'input-group-addon',
12289 html : this.fieldLabel
12297 if(this.indicatorpos == 'right'){
12302 //cls : 'input-group-addon',
12303 html : this.fieldLabel
12308 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12309 tooltip : 'This field is required',
12310 style : this.allowBlank ? ' display:none' : ''
12330 if (this.parentType === 'Navbar' && this.parent().bar) {
12331 cfg.cls += ' navbar-form';
12334 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12335 // on BS4 we do this only if not form
12336 cfg.cls += ' navbar-form';
12344 * return the real input element.
12346 inputEl: function ()
12348 return this.el.select('input.form-control',true).first();
12351 tooltipEl : function()
12353 return this.inputEl();
12356 indicatorEl : function()
12358 if (Roo.bootstrap.version == 4) {
12359 return false; // not enabled in v4 yet.
12362 var indicator = this.el.select('i.roo-required-indicator',true).first();
12372 setDisabled : function(v)
12374 var i = this.inputEl().dom;
12376 i.removeAttribute('disabled');
12380 i.setAttribute('disabled','true');
12382 initEvents : function()
12385 this.inputEl().on("keydown" , this.fireKey, this);
12386 this.inputEl().on("focus", this.onFocus, this);
12387 this.inputEl().on("blur", this.onBlur, this);
12389 this.inputEl().relayEvent('keyup', this);
12390 this.inputEl().relayEvent('paste', this);
12392 this.indicator = this.indicatorEl();
12394 if(this.indicator){
12395 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12398 // reference to original value for reset
12399 this.originalValue = this.getValue();
12400 //Roo.form.TextField.superclass.initEvents.call(this);
12401 if(this.validationEvent == 'keyup'){
12402 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12403 this.inputEl().on('keyup', this.filterValidation, this);
12405 else if(this.validationEvent !== false){
12406 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12409 if(this.selectOnFocus){
12410 this.on("focus", this.preFocus, this);
12413 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12414 this.inputEl().on("keypress", this.filterKeys, this);
12416 this.inputEl().relayEvent('keypress', this);
12419 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12420 this.el.on("click", this.autoSize, this);
12423 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12424 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12427 if (typeof(this.before) == 'object') {
12428 this.before.render(this.el.select('.roo-input-before',true).first());
12430 if (typeof(this.after) == 'object') {
12431 this.after.render(this.el.select('.roo-input-after',true).first());
12434 this.inputEl().on('change', this.onChange, this);
12437 filterValidation : function(e){
12438 if(!e.isNavKeyPress()){
12439 this.validationTask.delay(this.validationDelay);
12443 * Validates the field value
12444 * @return {Boolean} True if the value is valid, else false
12446 validate : function(){
12447 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12448 if(this.disabled || this.validateValue(this.getRawValue())){
12453 this.markInvalid();
12459 * Validates a value according to the field's validation rules and marks the field as invalid
12460 * if the validation fails
12461 * @param {Mixed} value The value to validate
12462 * @return {Boolean} True if the value is valid, else false
12464 validateValue : function(value)
12466 if(this.getVisibilityEl().hasClass('hidden')){
12470 if(value.length < 1) { // if it's blank
12471 if(this.allowBlank){
12477 if(value.length < this.minLength){
12480 if(value.length > this.maxLength){
12484 var vt = Roo.form.VTypes;
12485 if(!vt[this.vtype](value, this)){
12489 if(typeof this.validator == "function"){
12490 var msg = this.validator(value);
12494 if (typeof(msg) == 'string') {
12495 this.invalidText = msg;
12499 if(this.regex && !this.regex.test(value)){
12507 fireKey : function(e){
12508 //Roo.log('field ' + e.getKey());
12509 if(e.isNavKeyPress()){
12510 this.fireEvent("specialkey", this, e);
12513 focus : function (selectText){
12515 this.inputEl().focus();
12516 if(selectText === true){
12517 this.inputEl().dom.select();
12523 onFocus : function(){
12524 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12525 // this.el.addClass(this.focusClass);
12527 if(!this.hasFocus){
12528 this.hasFocus = true;
12529 this.startValue = this.getValue();
12530 this.fireEvent("focus", this);
12534 beforeBlur : Roo.emptyFn,
12538 onBlur : function(){
12540 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12541 //this.el.removeClass(this.focusClass);
12543 this.hasFocus = false;
12544 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12547 var v = this.getValue();
12548 if(String(v) !== String(this.startValue)){
12549 this.fireEvent('change', this, v, this.startValue);
12551 this.fireEvent("blur", this);
12554 onChange : function(e)
12556 var v = this.getValue();
12557 if(String(v) !== String(this.startValue)){
12558 this.fireEvent('change', this, v, this.startValue);
12564 * Resets the current field value to the originally loaded value and clears any validation messages
12566 reset : function(){
12567 this.setValue(this.originalValue);
12571 * Returns the name of the field
12572 * @return {Mixed} name The name field
12574 getName: function(){
12578 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12579 * @return {Mixed} value The field value
12581 getValue : function(){
12583 var v = this.inputEl().getValue();
12588 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12589 * @return {Mixed} value The field value
12591 getRawValue : function(){
12592 var v = this.inputEl().getValue();
12598 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12599 * @param {Mixed} value The value to set
12601 setRawValue : function(v){
12602 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12605 selectText : function(start, end){
12606 var v = this.getRawValue();
12608 start = start === undefined ? 0 : start;
12609 end = end === undefined ? v.length : end;
12610 var d = this.inputEl().dom;
12611 if(d.setSelectionRange){
12612 d.setSelectionRange(start, end);
12613 }else if(d.createTextRange){
12614 var range = d.createTextRange();
12615 range.moveStart("character", start);
12616 range.moveEnd("character", v.length-end);
12623 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12624 * @param {Mixed} value The value to set
12626 setValue : function(v){
12629 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12635 processValue : function(value){
12636 if(this.stripCharsRe){
12637 var newValue = value.replace(this.stripCharsRe, '');
12638 if(newValue !== value){
12639 this.setRawValue(newValue);
12646 preFocus : function(){
12648 if(this.selectOnFocus){
12649 this.inputEl().dom.select();
12652 filterKeys : function(e){
12653 var k = e.getKey();
12654 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12657 var c = e.getCharCode(), cc = String.fromCharCode(c);
12658 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12661 if(!this.maskRe.test(cc)){
12666 * Clear any invalid styles/messages for this field
12668 clearInvalid : function(){
12670 if(!this.el || this.preventMark){ // not rendered
12675 this.el.removeClass([this.invalidClass, 'is-invalid']);
12677 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12679 var feedback = this.el.select('.form-control-feedback', true).first();
12682 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12687 if(this.indicator){
12688 this.indicator.removeClass('visible');
12689 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12692 this.fireEvent('valid', this);
12696 * Mark this field as valid
12698 markValid : function()
12700 if(!this.el || this.preventMark){ // not rendered...
12704 this.el.removeClass([this.invalidClass, this.validClass]);
12705 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12707 var feedback = this.el.select('.form-control-feedback', true).first();
12710 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12713 if(this.indicator){
12714 this.indicator.removeClass('visible');
12715 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12723 if(this.allowBlank && !this.getRawValue().length){
12726 if (Roo.bootstrap.version == 3) {
12727 this.el.addClass(this.validClass);
12729 this.inputEl().addClass('is-valid');
12732 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12734 var feedback = this.el.select('.form-control-feedback', true).first();
12737 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12738 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12743 this.fireEvent('valid', this);
12747 * Mark this field as invalid
12748 * @param {String} msg The validation message
12750 markInvalid : function(msg)
12752 if(!this.el || this.preventMark){ // not rendered
12756 this.el.removeClass([this.invalidClass, this.validClass]);
12757 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12759 var feedback = this.el.select('.form-control-feedback', true).first();
12762 this.el.select('.form-control-feedback', true).first().removeClass(
12763 [this.invalidFeedbackClass, this.validFeedbackClass]);
12770 if(this.allowBlank && !this.getRawValue().length){
12774 if(this.indicator){
12775 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12776 this.indicator.addClass('visible');
12778 if (Roo.bootstrap.version == 3) {
12779 this.el.addClass(this.invalidClass);
12781 this.inputEl().addClass('is-invalid');
12786 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12788 var feedback = this.el.select('.form-control-feedback', true).first();
12791 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12793 if(this.getValue().length || this.forceFeedback){
12794 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12801 this.fireEvent('invalid', this, msg);
12804 SafariOnKeyDown : function(event)
12806 // this is a workaround for a password hang bug on chrome/ webkit.
12807 if (this.inputEl().dom.type != 'password') {
12811 var isSelectAll = false;
12813 if(this.inputEl().dom.selectionEnd > 0){
12814 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12816 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12817 event.preventDefault();
12822 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12824 event.preventDefault();
12825 // this is very hacky as keydown always get's upper case.
12827 var cc = String.fromCharCode(event.getCharCode());
12828 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12832 adjustWidth : function(tag, w){
12833 tag = tag.toLowerCase();
12834 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12835 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12836 if(tag == 'input'){
12839 if(tag == 'textarea'){
12842 }else if(Roo.isOpera){
12843 if(tag == 'input'){
12846 if(tag == 'textarea'){
12854 setFieldLabel : function(v)
12856 if(!this.rendered){
12860 if(this.indicatorEl()){
12861 var ar = this.el.select('label > span',true);
12863 if (ar.elements.length) {
12864 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12865 this.fieldLabel = v;
12869 var br = this.el.select('label',true);
12871 if(br.elements.length) {
12872 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12873 this.fieldLabel = v;
12877 Roo.log('Cannot Found any of label > span || label in input');
12881 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12882 this.fieldLabel = v;
12897 * @class Roo.bootstrap.TextArea
12898 * @extends Roo.bootstrap.Input
12899 * Bootstrap TextArea class
12900 * @cfg {Number} cols Specifies the visible width of a text area
12901 * @cfg {Number} rows Specifies the visible number of lines in a text area
12902 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12903 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12904 * @cfg {string} html text
12907 * Create a new TextArea
12908 * @param {Object} config The config object
12911 Roo.bootstrap.TextArea = function(config){
12912 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12916 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12926 getAutoCreate : function(){
12928 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12934 if(this.inputType != 'hidden'){
12935 cfg.cls = 'form-group' //input-group
12943 value : this.value || '',
12944 html: this.html || '',
12945 cls : 'form-control',
12946 placeholder : this.placeholder || ''
12950 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12951 input.maxLength = this.maxLength;
12955 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12959 input.cols = this.cols;
12962 if (this.readOnly) {
12963 input.readonly = true;
12967 input.name = this.name;
12971 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12975 ['xs','sm','md','lg'].map(function(size){
12976 if (settings[size]) {
12977 cfg.cls += ' col-' + size + '-' + settings[size];
12981 var inputblock = input;
12983 if(this.hasFeedback && !this.allowBlank){
12987 cls: 'glyphicon form-control-feedback'
12991 cls : 'has-feedback',
13000 if (this.before || this.after) {
13003 cls : 'input-group',
13007 inputblock.cn.push({
13009 cls : 'input-group-addon',
13014 inputblock.cn.push(input);
13016 if(this.hasFeedback && !this.allowBlank){
13017 inputblock.cls += ' has-feedback';
13018 inputblock.cn.push(feedback);
13022 inputblock.cn.push({
13024 cls : 'input-group-addon',
13031 if (align ==='left' && this.fieldLabel.length) {
13036 cls : 'control-label',
13037 html : this.fieldLabel
13048 if(this.labelWidth > 12){
13049 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13052 if(this.labelWidth < 13 && this.labelmd == 0){
13053 this.labelmd = this.labelWidth;
13056 if(this.labellg > 0){
13057 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13058 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13061 if(this.labelmd > 0){
13062 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13063 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13066 if(this.labelsm > 0){
13067 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13068 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13071 if(this.labelxs > 0){
13072 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13073 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13076 } else if ( this.fieldLabel.length) {
13081 //cls : 'input-group-addon',
13082 html : this.fieldLabel
13100 if (this.disabled) {
13101 input.disabled=true;
13108 * return the real textarea element.
13110 inputEl: function ()
13112 return this.el.select('textarea.form-control',true).first();
13116 * Clear any invalid styles/messages for this field
13118 clearInvalid : function()
13121 if(!this.el || this.preventMark){ // not rendered
13125 var label = this.el.select('label', true).first();
13126 var icon = this.el.select('i.fa-star', true).first();
13131 this.el.removeClass( this.validClass);
13132 this.inputEl().removeClass('is-invalid');
13134 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13136 var feedback = this.el.select('.form-control-feedback', true).first();
13139 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13144 this.fireEvent('valid', this);
13148 * Mark this field as valid
13150 markValid : function()
13152 if(!this.el || this.preventMark){ // not rendered
13156 this.el.removeClass([this.invalidClass, this.validClass]);
13157 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13159 var feedback = this.el.select('.form-control-feedback', true).first();
13162 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13165 if(this.disabled || this.allowBlank){
13169 var label = this.el.select('label', true).first();
13170 var icon = this.el.select('i.fa-star', true).first();
13175 if (Roo.bootstrap.version == 3) {
13176 this.el.addClass(this.validClass);
13178 this.inputEl().addClass('is-valid');
13182 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13184 var feedback = this.el.select('.form-control-feedback', true).first();
13187 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13188 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13193 this.fireEvent('valid', this);
13197 * Mark this field as invalid
13198 * @param {String} msg The validation message
13200 markInvalid : function(msg)
13202 if(!this.el || this.preventMark){ // not rendered
13206 this.el.removeClass([this.invalidClass, this.validClass]);
13207 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13209 var feedback = this.el.select('.form-control-feedback', true).first();
13212 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13215 if(this.disabled || this.allowBlank){
13219 var label = this.el.select('label', true).first();
13220 var icon = this.el.select('i.fa-star', true).first();
13222 if(!this.getValue().length && label && !icon){
13223 this.el.createChild({
13225 cls : 'text-danger fa fa-lg fa-star',
13226 tooltip : 'This field is required',
13227 style : 'margin-right:5px;'
13231 if (Roo.bootstrap.version == 3) {
13232 this.el.addClass(this.invalidClass);
13234 this.inputEl().addClass('is-invalid');
13237 // fixme ... this may be depricated need to test..
13238 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13240 var feedback = this.el.select('.form-control-feedback', true).first();
13243 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13245 if(this.getValue().length || this.forceFeedback){
13246 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13253 this.fireEvent('invalid', this, msg);
13261 * trigger field - base class for combo..
13266 * @class Roo.bootstrap.TriggerField
13267 * @extends Roo.bootstrap.Input
13268 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13269 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13270 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13271 * for which you can provide a custom implementation. For example:
13273 var trigger = new Roo.bootstrap.TriggerField();
13274 trigger.onTriggerClick = myTriggerFn;
13275 trigger.applyTo('my-field');
13278 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13279 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13280 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13281 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13282 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13285 * Create a new TriggerField.
13286 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13287 * to the base TextField)
13289 Roo.bootstrap.TriggerField = function(config){
13290 this.mimicing = false;
13291 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13294 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13296 * @cfg {String} triggerClass A CSS class to apply to the trigger
13299 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13304 * @cfg {Boolean} removable (true|false) special filter default false
13308 /** @cfg {Boolean} grow @hide */
13309 /** @cfg {Number} growMin @hide */
13310 /** @cfg {Number} growMax @hide */
13316 autoSize: Roo.emptyFn,
13320 deferHeight : true,
13323 actionMode : 'wrap',
13328 getAutoCreate : function(){
13330 var align = this.labelAlign || this.parentLabelAlign();
13335 cls: 'form-group' //input-group
13342 type : this.inputType,
13343 cls : 'form-control',
13344 autocomplete: 'new-password',
13345 placeholder : this.placeholder || ''
13349 input.name = this.name;
13352 input.cls += ' input-' + this.size;
13355 if (this.disabled) {
13356 input.disabled=true;
13359 var inputblock = input;
13361 if(this.hasFeedback && !this.allowBlank){
13365 cls: 'glyphicon form-control-feedback'
13368 if(this.removable && !this.editable ){
13370 cls : 'has-feedback',
13376 cls : 'roo-combo-removable-btn close'
13383 cls : 'has-feedback',
13392 if(this.removable && !this.editable ){
13394 cls : 'roo-removable',
13400 cls : 'roo-combo-removable-btn close'
13407 if (this.before || this.after) {
13410 cls : 'input-group',
13414 inputblock.cn.push({
13416 cls : 'input-group-addon input-group-prepend input-group-text',
13421 inputblock.cn.push(input);
13423 if(this.hasFeedback && !this.allowBlank){
13424 inputblock.cls += ' has-feedback';
13425 inputblock.cn.push(feedback);
13429 inputblock.cn.push({
13431 cls : 'input-group-addon input-group-append input-group-text',
13440 var ibwrap = inputblock;
13445 cls: 'roo-select2-choices',
13449 cls: 'roo-select2-search-field',
13461 cls: 'roo-select2-container input-group',
13466 cls: 'form-hidden-field'
13472 if(!this.multiple && this.showToggleBtn){
13478 if (this.caret != false) {
13481 cls: 'fa fa-' + this.caret
13488 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13490 Roo.bootstrap.version == 3 ? caret : '',
13493 cls: 'combobox-clear',
13507 combobox.cls += ' roo-select2-container-multi';
13511 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13512 tooltip : 'This field is required'
13514 if (Roo.bootstrap.version == 4) {
13517 style : 'display:none'
13522 if (align ==='left' && this.fieldLabel.length) {
13524 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13531 cls : 'control-label',
13532 html : this.fieldLabel
13544 var labelCfg = cfg.cn[1];
13545 var contentCfg = cfg.cn[2];
13547 if(this.indicatorpos == 'right'){
13552 cls : 'control-label',
13556 html : this.fieldLabel
13570 labelCfg = cfg.cn[0];
13571 contentCfg = cfg.cn[1];
13574 if(this.labelWidth > 12){
13575 labelCfg.style = "width: " + this.labelWidth + 'px';
13578 if(this.labelWidth < 13 && this.labelmd == 0){
13579 this.labelmd = this.labelWidth;
13582 if(this.labellg > 0){
13583 labelCfg.cls += ' col-lg-' + this.labellg;
13584 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13587 if(this.labelmd > 0){
13588 labelCfg.cls += ' col-md-' + this.labelmd;
13589 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13592 if(this.labelsm > 0){
13593 labelCfg.cls += ' col-sm-' + this.labelsm;
13594 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13597 if(this.labelxs > 0){
13598 labelCfg.cls += ' col-xs-' + this.labelxs;
13599 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13602 } else if ( this.fieldLabel.length) {
13603 // Roo.log(" label");
13608 //cls : 'input-group-addon',
13609 html : this.fieldLabel
13617 if(this.indicatorpos == 'right'){
13625 html : this.fieldLabel
13639 // Roo.log(" no label && no align");
13646 ['xs','sm','md','lg'].map(function(size){
13647 if (settings[size]) {
13648 cfg.cls += ' col-' + size + '-' + settings[size];
13659 onResize : function(w, h){
13660 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13661 // if(typeof w == 'number'){
13662 // var x = w - this.trigger.getWidth();
13663 // this.inputEl().setWidth(this.adjustWidth('input', x));
13664 // this.trigger.setStyle('left', x+'px');
13669 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13672 getResizeEl : function(){
13673 return this.inputEl();
13677 getPositionEl : function(){
13678 return this.inputEl();
13682 alignErrorIcon : function(){
13683 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13687 initEvents : function(){
13691 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13692 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13693 if(!this.multiple && this.showToggleBtn){
13694 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13695 if(this.hideTrigger){
13696 this.trigger.setDisplayed(false);
13698 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13702 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13705 if(this.removable && !this.editable && !this.tickable){
13706 var close = this.closeTriggerEl();
13709 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13710 close.on('click', this.removeBtnClick, this, close);
13714 //this.trigger.addClassOnOver('x-form-trigger-over');
13715 //this.trigger.addClassOnClick('x-form-trigger-click');
13718 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13722 closeTriggerEl : function()
13724 var close = this.el.select('.roo-combo-removable-btn', true).first();
13725 return close ? close : false;
13728 removeBtnClick : function(e, h, el)
13730 e.preventDefault();
13732 if(this.fireEvent("remove", this) !== false){
13734 this.fireEvent("afterremove", this)
13738 createList : function()
13740 this.list = Roo.get(document.body).createChild({
13741 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13742 cls: 'typeahead typeahead-long dropdown-menu shadow',
13743 style: 'display:none'
13746 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13751 initTrigger : function(){
13756 onDestroy : function(){
13758 this.trigger.removeAllListeners();
13759 // this.trigger.remove();
13762 // this.wrap.remove();
13764 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13768 onFocus : function(){
13769 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13771 if(!this.mimicing){
13772 this.wrap.addClass('x-trigger-wrap-focus');
13773 this.mimicing = true;
13774 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13775 if(this.monitorTab){
13776 this.el.on("keydown", this.checkTab, this);
13783 checkTab : function(e){
13784 if(e.getKey() == e.TAB){
13785 this.triggerBlur();
13790 onBlur : function(){
13795 mimicBlur : function(e, t){
13797 if(!this.wrap.contains(t) && this.validateBlur()){
13798 this.triggerBlur();
13804 triggerBlur : function(){
13805 this.mimicing = false;
13806 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13807 if(this.monitorTab){
13808 this.el.un("keydown", this.checkTab, this);
13810 //this.wrap.removeClass('x-trigger-wrap-focus');
13811 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13815 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13816 validateBlur : function(e, t){
13821 onDisable : function(){
13822 this.inputEl().dom.disabled = true;
13823 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13825 // this.wrap.addClass('x-item-disabled');
13830 onEnable : function(){
13831 this.inputEl().dom.disabled = false;
13832 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13834 // this.el.removeClass('x-item-disabled');
13839 onShow : function(){
13840 var ae = this.getActionEl();
13843 ae.dom.style.display = '';
13844 ae.dom.style.visibility = 'visible';
13850 onHide : function(){
13851 var ae = this.getActionEl();
13852 ae.dom.style.display = 'none';
13856 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13857 * by an implementing function.
13859 * @param {EventObject} e
13861 onTriggerClick : Roo.emptyFn
13869 * @class Roo.bootstrap.CardUploader
13870 * @extends Roo.bootstrap.Button
13871 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13872 * @cfg {Number} errorTimeout default 3000
13873 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13874 * @cfg {Array} html The button text.
13878 * Create a new CardUploader
13879 * @param {Object} config The config object
13882 Roo.bootstrap.CardUploader = function(config){
13886 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13889 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13897 * When a image is clicked on - and needs to display a slideshow or similar..
13898 * @param {Roo.bootstrap.Card} this
13899 * @param {Object} The image information data
13905 * When a the download link is clicked
13906 * @param {Roo.bootstrap.Card} this
13907 * @param {Object} The image information data contains
13914 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13917 errorTimeout : 3000,
13921 fileCollection : false,
13924 getAutoCreate : function()
13928 cls :'form-group' ,
13933 //cls : 'input-group-addon',
13934 html : this.fieldLabel
13942 value : this.value,
13943 cls : 'd-none form-control'
13948 multiple : 'multiple',
13950 cls : 'd-none roo-card-upload-selector'
13954 cls : 'roo-card-uploader-button-container w-100 mb-2'
13957 cls : 'card-columns roo-card-uploader-container'
13967 getChildContainer : function() /// what children are added to.
13969 return this.containerEl;
13972 getButtonContainer : function() /// what children are added to.
13974 return this.el.select(".roo-card-uploader-button-container").first();
13977 initEvents : function()
13980 Roo.bootstrap.Input.prototype.initEvents.call(this);
13984 xns: Roo.bootstrap,
13987 container_method : 'getButtonContainer' ,
13988 html : this.html, // fix changable?
13991 'click' : function(btn, e) {
14000 this.urlAPI = (window.createObjectURL && window) ||
14001 (window.URL && URL.revokeObjectURL && URL) ||
14002 (window.webkitURL && webkitURL);
14007 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14009 this.selectorEl.on('change', this.onFileSelected, this);
14012 this.images.forEach(function(img) {
14015 this.images = false;
14017 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14023 onClick : function(e)
14025 e.preventDefault();
14027 this.selectorEl.dom.click();
14031 onFileSelected : function(e)
14033 e.preventDefault();
14035 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14039 Roo.each(this.selectorEl.dom.files, function(file){
14040 this.addFile(file);
14049 addFile : function(file)
14052 if(typeof(file) === 'string'){
14053 throw "Add file by name?"; // should not happen
14057 if(!file || !this.urlAPI){
14067 var url = _this.urlAPI.createObjectURL( file);
14070 id : Roo.bootstrap.CardUploader.ID--,
14071 is_uploaded : false,
14075 mimetype : file.type,
14083 * addCard - add an Attachment to the uploader
14084 * @param data - the data about the image to upload
14088 title : "Title of file",
14089 is_uploaded : false,
14090 src : "http://.....",
14091 srcfile : { the File upload object },
14092 mimetype : file.type,
14095 .. any other data...
14101 addCard : function (data)
14103 // hidden input element?
14104 // if the file is not an image...
14105 //then we need to use something other that and header_image
14110 xns : Roo.bootstrap,
14111 xtype : 'CardFooter',
14114 xns : Roo.bootstrap,
14120 xns : Roo.bootstrap,
14122 html : String.format("<small>{0}</small>", data.title),
14123 cls : 'col-10 text-left',
14128 click : function() {
14130 t.fireEvent( "download", t, data );
14136 xns : Roo.bootstrap,
14138 style: 'max-height: 28px; ',
14144 click : function() {
14145 t.removeCard(data.id)
14157 var cn = this.addxtype(
14160 xns : Roo.bootstrap,
14163 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14164 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14165 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14170 initEvents : function() {
14171 Roo.bootstrap.Card.prototype.initEvents.call(this);
14173 this.imgEl = this.el.select('.card-img-top').first();
14175 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14176 this.imgEl.set({ 'pointer' : 'cursor' });
14179 this.getCardFooter().addClass('p-1');
14186 // dont' really need ot update items.
14187 // this.items.push(cn);
14188 this.fileCollection.add(cn);
14190 if (!data.srcfile) {
14191 this.updateInput();
14196 var reader = new FileReader();
14197 reader.addEventListener("load", function() {
14198 data.srcdata = reader.result;
14201 reader.readAsDataURL(data.srcfile);
14206 removeCard : function(id)
14209 var card = this.fileCollection.get(id);
14210 card.data.is_deleted = 1;
14211 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14212 //this.fileCollection.remove(card);
14213 //this.items = this.items.filter(function(e) { return e != card });
14214 // dont' really need ot update items.
14215 card.el.dom.parentNode.removeChild(card.el.dom);
14216 this.updateInput();
14222 this.fileCollection.each(function(card) {
14223 if (card.el.dom && card.el.dom.parentNode) {
14224 card.el.dom.parentNode.removeChild(card.el.dom);
14227 this.fileCollection.clear();
14228 this.updateInput();
14231 updateInput : function()
14234 this.fileCollection.each(function(e) {
14238 this.inputEl().dom.value = JSON.stringify(data);
14248 Roo.bootstrap.CardUploader.ID = -1;/*
14250 * Ext JS Library 1.1.1
14251 * Copyright(c) 2006-2007, Ext JS, LLC.
14253 * Originally Released Under LGPL - original licence link has changed is not relivant.
14256 * <script type="text/javascript">
14261 * @class Roo.data.SortTypes
14263 * Defines the default sorting (casting?) comparison functions used when sorting data.
14265 Roo.data.SortTypes = {
14267 * Default sort that does nothing
14268 * @param {Mixed} s The value being converted
14269 * @return {Mixed} The comparison value
14271 none : function(s){
14276 * The regular expression used to strip tags
14280 stripTagsRE : /<\/?[^>]+>/gi,
14283 * Strips all HTML tags to sort on text only
14284 * @param {Mixed} s The value being converted
14285 * @return {String} The comparison value
14287 asText : function(s){
14288 return String(s).replace(this.stripTagsRE, "");
14292 * Strips all HTML tags to sort on text only - Case insensitive
14293 * @param {Mixed} s The value being converted
14294 * @return {String} The comparison value
14296 asUCText : function(s){
14297 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14301 * Case insensitive string
14302 * @param {Mixed} s The value being converted
14303 * @return {String} The comparison value
14305 asUCString : function(s) {
14306 return String(s).toUpperCase();
14311 * @param {Mixed} s The value being converted
14312 * @return {Number} The comparison value
14314 asDate : function(s) {
14318 if(s instanceof Date){
14319 return s.getTime();
14321 return Date.parse(String(s));
14326 * @param {Mixed} s The value being converted
14327 * @return {Float} The comparison value
14329 asFloat : function(s) {
14330 var val = parseFloat(String(s).replace(/,/g, ""));
14339 * @param {Mixed} s The value being converted
14340 * @return {Number} The comparison value
14342 asInt : function(s) {
14343 var val = parseInt(String(s).replace(/,/g, ""));
14351 * Ext JS Library 1.1.1
14352 * Copyright(c) 2006-2007, Ext JS, LLC.
14354 * Originally Released Under LGPL - original licence link has changed is not relivant.
14357 * <script type="text/javascript">
14361 * @class Roo.data.Record
14362 * Instances of this class encapsulate both record <em>definition</em> information, and record
14363 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14364 * to access Records cached in an {@link Roo.data.Store} object.<br>
14366 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14367 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14370 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14372 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14373 * {@link #create}. The parameters are the same.
14374 * @param {Array} data An associative Array of data values keyed by the field name.
14375 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14376 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14377 * not specified an integer id is generated.
14379 Roo.data.Record = function(data, id){
14380 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14385 * Generate a constructor for a specific record layout.
14386 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14387 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14388 * Each field definition object may contain the following properties: <ul>
14389 * <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,
14390 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14391 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14392 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14393 * is being used, then this is a string containing the javascript expression to reference the data relative to
14394 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14395 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14396 * this may be omitted.</p></li>
14397 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14398 * <ul><li>auto (Default, implies no conversion)</li>
14403 * <li>date</li></ul></p></li>
14404 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14405 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14406 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14407 * by the Reader into an object that will be stored in the Record. It is passed the
14408 * following parameters:<ul>
14409 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14411 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14413 * <br>usage:<br><pre><code>
14414 var TopicRecord = Roo.data.Record.create(
14415 {name: 'title', mapping: 'topic_title'},
14416 {name: 'author', mapping: 'username'},
14417 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14418 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14419 {name: 'lastPoster', mapping: 'user2'},
14420 {name: 'excerpt', mapping: 'post_text'}
14423 var myNewRecord = new TopicRecord({
14424 title: 'Do my job please',
14427 lastPost: new Date(),
14428 lastPoster: 'Animal',
14429 excerpt: 'No way dude!'
14431 myStore.add(myNewRecord);
14436 Roo.data.Record.create = function(o){
14437 var f = function(){
14438 f.superclass.constructor.apply(this, arguments);
14440 Roo.extend(f, Roo.data.Record);
14441 var p = f.prototype;
14442 p.fields = new Roo.util.MixedCollection(false, function(field){
14445 for(var i = 0, len = o.length; i < len; i++){
14446 p.fields.add(new Roo.data.Field(o[i]));
14448 f.getField = function(name){
14449 return p.fields.get(name);
14454 Roo.data.Record.AUTO_ID = 1000;
14455 Roo.data.Record.EDIT = 'edit';
14456 Roo.data.Record.REJECT = 'reject';
14457 Roo.data.Record.COMMIT = 'commit';
14459 Roo.data.Record.prototype = {
14461 * Readonly flag - true if this record has been modified.
14470 join : function(store){
14471 this.store = store;
14475 * Set the named field to the specified value.
14476 * @param {String} name The name of the field to set.
14477 * @param {Object} value The value to set the field to.
14479 set : function(name, value){
14480 if(this.data[name] == value){
14484 if(!this.modified){
14485 this.modified = {};
14487 if(typeof this.modified[name] == 'undefined'){
14488 this.modified[name] = this.data[name];
14490 this.data[name] = value;
14491 if(!this.editing && this.store){
14492 this.store.afterEdit(this);
14497 * Get the value of the named field.
14498 * @param {String} name The name of the field to get the value of.
14499 * @return {Object} The value of the field.
14501 get : function(name){
14502 return this.data[name];
14506 beginEdit : function(){
14507 this.editing = true;
14508 this.modified = {};
14512 cancelEdit : function(){
14513 this.editing = false;
14514 delete this.modified;
14518 endEdit : function(){
14519 this.editing = false;
14520 if(this.dirty && this.store){
14521 this.store.afterEdit(this);
14526 * Usually called by the {@link Roo.data.Store} which owns the Record.
14527 * Rejects all changes made to the Record since either creation, or the last commit operation.
14528 * Modified fields are reverted to their original values.
14530 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14531 * of reject operations.
14533 reject : function(){
14534 var m = this.modified;
14536 if(typeof m[n] != "function"){
14537 this.data[n] = m[n];
14540 this.dirty = false;
14541 delete this.modified;
14542 this.editing = false;
14544 this.store.afterReject(this);
14549 * Usually called by the {@link Roo.data.Store} which owns the Record.
14550 * Commits all changes made to the Record since either creation, or the last commit operation.
14552 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14553 * of commit operations.
14555 commit : function(){
14556 this.dirty = false;
14557 delete this.modified;
14558 this.editing = false;
14560 this.store.afterCommit(this);
14565 hasError : function(){
14566 return this.error != null;
14570 clearError : function(){
14575 * Creates a copy of this record.
14576 * @param {String} id (optional) A new record id if you don't want to use this record's id
14579 copy : function(newId) {
14580 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14584 * Ext JS Library 1.1.1
14585 * Copyright(c) 2006-2007, Ext JS, LLC.
14587 * Originally Released Under LGPL - original licence link has changed is not relivant.
14590 * <script type="text/javascript">
14596 * @class Roo.data.Store
14597 * @extends Roo.util.Observable
14598 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14599 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14601 * 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
14602 * has no knowledge of the format of the data returned by the Proxy.<br>
14604 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14605 * instances from the data object. These records are cached and made available through accessor functions.
14607 * Creates a new Store.
14608 * @param {Object} config A config object containing the objects needed for the Store to access data,
14609 * and read the data into Records.
14611 Roo.data.Store = function(config){
14612 this.data = new Roo.util.MixedCollection(false);
14613 this.data.getKey = function(o){
14616 this.baseParams = {};
14618 this.paramNames = {
14623 "multisort" : "_multisort"
14626 if(config && config.data){
14627 this.inlineData = config.data;
14628 delete config.data;
14631 Roo.apply(this, config);
14633 if(this.reader){ // reader passed
14634 this.reader = Roo.factory(this.reader, Roo.data);
14635 this.reader.xmodule = this.xmodule || false;
14636 if(!this.recordType){
14637 this.recordType = this.reader.recordType;
14639 if(this.reader.onMetaChange){
14640 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14644 if(this.recordType){
14645 this.fields = this.recordType.prototype.fields;
14647 this.modified = [];
14651 * @event datachanged
14652 * Fires when the data cache has changed, and a widget which is using this Store
14653 * as a Record cache should refresh its view.
14654 * @param {Store} this
14656 datachanged : true,
14658 * @event metachange
14659 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14660 * @param {Store} this
14661 * @param {Object} meta The JSON metadata
14666 * Fires when Records have been added to the Store
14667 * @param {Store} this
14668 * @param {Roo.data.Record[]} records The array of Records added
14669 * @param {Number} index The index at which the record(s) were added
14674 * Fires when a Record has been removed from the Store
14675 * @param {Store} this
14676 * @param {Roo.data.Record} record The Record that was removed
14677 * @param {Number} index The index at which the record was removed
14682 * Fires when a Record has been updated
14683 * @param {Store} this
14684 * @param {Roo.data.Record} record The Record that was updated
14685 * @param {String} operation The update operation being performed. Value may be one of:
14687 Roo.data.Record.EDIT
14688 Roo.data.Record.REJECT
14689 Roo.data.Record.COMMIT
14695 * Fires when the data cache has been cleared.
14696 * @param {Store} this
14700 * @event beforeload
14701 * Fires before a request is made for a new data object. If the beforeload handler returns false
14702 * the load action will be canceled.
14703 * @param {Store} this
14704 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14708 * @event beforeloadadd
14709 * Fires after a new set of Records has been loaded.
14710 * @param {Store} this
14711 * @param {Roo.data.Record[]} records The Records that were loaded
14712 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14714 beforeloadadd : true,
14717 * Fires after a new set of Records has been loaded, before they are added to the store.
14718 * @param {Store} this
14719 * @param {Roo.data.Record[]} records The Records that were loaded
14720 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14721 * @params {Object} return from reader
14725 * @event loadexception
14726 * Fires if an exception occurs in the Proxy during loading.
14727 * Called with the signature of the Proxy's "loadexception" event.
14728 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14731 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14732 * @param {Object} load options
14733 * @param {Object} jsonData from your request (normally this contains the Exception)
14735 loadexception : true
14739 this.proxy = Roo.factory(this.proxy, Roo.data);
14740 this.proxy.xmodule = this.xmodule || false;
14741 this.relayEvents(this.proxy, ["loadexception"]);
14743 this.sortToggle = {};
14744 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14746 Roo.data.Store.superclass.constructor.call(this);
14748 if(this.inlineData){
14749 this.loadData(this.inlineData);
14750 delete this.inlineData;
14754 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14756 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14757 * without a remote query - used by combo/forms at present.
14761 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14764 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14767 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
14768 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14771 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14772 * on any HTTP request
14775 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14778 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14782 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14783 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14785 remoteSort : false,
14788 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14789 * loaded or when a record is removed. (defaults to false).
14791 pruneModifiedRecords : false,
14794 lastOptions : null,
14797 * Add Records to the Store and fires the add event.
14798 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14800 add : function(records){
14801 records = [].concat(records);
14802 for(var i = 0, len = records.length; i < len; i++){
14803 records[i].join(this);
14805 var index = this.data.length;
14806 this.data.addAll(records);
14807 this.fireEvent("add", this, records, index);
14811 * Remove a Record from the Store and fires the remove event.
14812 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14814 remove : function(record){
14815 var index = this.data.indexOf(record);
14816 this.data.removeAt(index);
14818 if(this.pruneModifiedRecords){
14819 this.modified.remove(record);
14821 this.fireEvent("remove", this, record, index);
14825 * Remove all Records from the Store and fires the clear event.
14827 removeAll : function(){
14829 if(this.pruneModifiedRecords){
14830 this.modified = [];
14832 this.fireEvent("clear", this);
14836 * Inserts Records to the Store at the given index and fires the add event.
14837 * @param {Number} index The start index at which to insert the passed Records.
14838 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14840 insert : function(index, records){
14841 records = [].concat(records);
14842 for(var i = 0, len = records.length; i < len; i++){
14843 this.data.insert(index, records[i]);
14844 records[i].join(this);
14846 this.fireEvent("add", this, records, index);
14850 * Get the index within the cache of the passed Record.
14851 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14852 * @return {Number} The index of the passed Record. Returns -1 if not found.
14854 indexOf : function(record){
14855 return this.data.indexOf(record);
14859 * Get the index within the cache of the Record with the passed id.
14860 * @param {String} id The id of the Record to find.
14861 * @return {Number} The index of the Record. Returns -1 if not found.
14863 indexOfId : function(id){
14864 return this.data.indexOfKey(id);
14868 * Get the Record with the specified id.
14869 * @param {String} id The id of the Record to find.
14870 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14872 getById : function(id){
14873 return this.data.key(id);
14877 * Get the Record at the specified index.
14878 * @param {Number} index The index of the Record to find.
14879 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14881 getAt : function(index){
14882 return this.data.itemAt(index);
14886 * Returns a range of Records between specified indices.
14887 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14888 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14889 * @return {Roo.data.Record[]} An array of Records
14891 getRange : function(start, end){
14892 return this.data.getRange(start, end);
14896 storeOptions : function(o){
14897 o = Roo.apply({}, o);
14900 this.lastOptions = o;
14904 * Loads the Record cache from the configured Proxy using the configured Reader.
14906 * If using remote paging, then the first load call must specify the <em>start</em>
14907 * and <em>limit</em> properties in the options.params property to establish the initial
14908 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14910 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14911 * and this call will return before the new data has been loaded. Perform any post-processing
14912 * in a callback function, or in a "load" event handler.</strong>
14914 * @param {Object} options An object containing properties which control loading options:<ul>
14915 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14916 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14917 * passed the following arguments:<ul>
14918 * <li>r : Roo.data.Record[]</li>
14919 * <li>options: Options object from the load call</li>
14920 * <li>success: Boolean success indicator</li></ul></li>
14921 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14922 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14925 load : function(options){
14926 options = options || {};
14927 if(this.fireEvent("beforeload", this, options) !== false){
14928 this.storeOptions(options);
14929 var p = Roo.apply(options.params || {}, this.baseParams);
14930 // if meta was not loaded from remote source.. try requesting it.
14931 if (!this.reader.metaFromRemote) {
14932 p._requestMeta = 1;
14934 if(this.sortInfo && this.remoteSort){
14935 var pn = this.paramNames;
14936 p[pn["sort"]] = this.sortInfo.field;
14937 p[pn["dir"]] = this.sortInfo.direction;
14939 if (this.multiSort) {
14940 var pn = this.paramNames;
14941 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14944 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14949 * Reloads the Record cache from the configured Proxy using the configured Reader and
14950 * the options from the last load operation performed.
14951 * @param {Object} options (optional) An object containing properties which may override the options
14952 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14953 * the most recently used options are reused).
14955 reload : function(options){
14956 this.load(Roo.applyIf(options||{}, this.lastOptions));
14960 // Called as a callback by the Reader during a load operation.
14961 loadRecords : function(o, options, success){
14962 if(!o || success === false){
14963 if(success !== false){
14964 this.fireEvent("load", this, [], options, o);
14966 if(options.callback){
14967 options.callback.call(options.scope || this, [], options, false);
14971 // if data returned failure - throw an exception.
14972 if (o.success === false) {
14973 // show a message if no listener is registered.
14974 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14975 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14977 // loadmask wil be hooked into this..
14978 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14981 var r = o.records, t = o.totalRecords || r.length;
14983 this.fireEvent("beforeloadadd", this, r, options, o);
14985 if(!options || options.add !== true){
14986 if(this.pruneModifiedRecords){
14987 this.modified = [];
14989 for(var i = 0, len = r.length; i < len; i++){
14993 this.data = this.snapshot;
14994 delete this.snapshot;
14997 this.data.addAll(r);
14998 this.totalLength = t;
15000 this.fireEvent("datachanged", this);
15002 this.totalLength = Math.max(t, this.data.length+r.length);
15006 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15008 var e = new Roo.data.Record({});
15010 e.set(this.parent.displayField, this.parent.emptyTitle);
15011 e.set(this.parent.valueField, '');
15016 this.fireEvent("load", this, r, options, o);
15017 if(options.callback){
15018 options.callback.call(options.scope || this, r, options, true);
15024 * Loads data from a passed data block. A Reader which understands the format of the data
15025 * must have been configured in the constructor.
15026 * @param {Object} data The data block from which to read the Records. The format of the data expected
15027 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15028 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15030 loadData : function(o, append){
15031 var r = this.reader.readRecords(o);
15032 this.loadRecords(r, {add: append}, true);
15036 * using 'cn' the nested child reader read the child array into it's child stores.
15037 * @param {Object} rec The record with a 'children array
15039 loadDataFromChildren : function(rec)
15041 this.loadData(this.reader.toLoadData(rec));
15046 * Gets the number of cached records.
15048 * <em>If using paging, this may not be the total size of the dataset. If the data object
15049 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15050 * the data set size</em>
15052 getCount : function(){
15053 return this.data.length || 0;
15057 * Gets the total number of records in the dataset as returned by the server.
15059 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15060 * the dataset size</em>
15062 getTotalCount : function(){
15063 return this.totalLength || 0;
15067 * Returns the sort state of the Store as an object with two properties:
15069 field {String} The name of the field by which the Records are sorted
15070 direction {String} The sort order, "ASC" or "DESC"
15073 getSortState : function(){
15074 return this.sortInfo;
15078 applySort : function(){
15079 if(this.sortInfo && !this.remoteSort){
15080 var s = this.sortInfo, f = s.field;
15081 var st = this.fields.get(f).sortType;
15082 var fn = function(r1, r2){
15083 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15084 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15086 this.data.sort(s.direction, fn);
15087 if(this.snapshot && this.snapshot != this.data){
15088 this.snapshot.sort(s.direction, fn);
15094 * Sets the default sort column and order to be used by the next load operation.
15095 * @param {String} fieldName The name of the field to sort by.
15096 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15098 setDefaultSort : function(field, dir){
15099 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15103 * Sort the Records.
15104 * If remote sorting is used, the sort is performed on the server, and the cache is
15105 * reloaded. If local sorting is used, the cache is sorted internally.
15106 * @param {String} fieldName The name of the field to sort by.
15107 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15109 sort : function(fieldName, dir){
15110 var f = this.fields.get(fieldName);
15112 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15114 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15115 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15120 this.sortToggle[f.name] = dir;
15121 this.sortInfo = {field: f.name, direction: dir};
15122 if(!this.remoteSort){
15124 this.fireEvent("datachanged", this);
15126 this.load(this.lastOptions);
15131 * Calls the specified function for each of the Records in the cache.
15132 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15133 * Returning <em>false</em> aborts and exits the iteration.
15134 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15136 each : function(fn, scope){
15137 this.data.each(fn, scope);
15141 * Gets all records modified since the last commit. Modified records are persisted across load operations
15142 * (e.g., during paging).
15143 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15145 getModifiedRecords : function(){
15146 return this.modified;
15150 createFilterFn : function(property, value, anyMatch){
15151 if(!value.exec){ // not a regex
15152 value = String(value);
15153 if(value.length == 0){
15156 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15158 return function(r){
15159 return value.test(r.data[property]);
15164 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15165 * @param {String} property A field on your records
15166 * @param {Number} start The record index to start at (defaults to 0)
15167 * @param {Number} end The last record index to include (defaults to length - 1)
15168 * @return {Number} The sum
15170 sum : function(property, start, end){
15171 var rs = this.data.items, v = 0;
15172 start = start || 0;
15173 end = (end || end === 0) ? end : rs.length-1;
15175 for(var i = start; i <= end; i++){
15176 v += (rs[i].data[property] || 0);
15182 * Filter the records by a specified property.
15183 * @param {String} field A field on your records
15184 * @param {String/RegExp} value Either a string that the field
15185 * should start with or a RegExp to test against the field
15186 * @param {Boolean} anyMatch True to match any part not just the beginning
15188 filter : function(property, value, anyMatch){
15189 var fn = this.createFilterFn(property, value, anyMatch);
15190 return fn ? this.filterBy(fn) : this.clearFilter();
15194 * Filter by a function. The specified function will be called with each
15195 * record in this data source. If the function returns true the record is included,
15196 * otherwise it is filtered.
15197 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15198 * @param {Object} scope (optional) The scope of the function (defaults to this)
15200 filterBy : function(fn, scope){
15201 this.snapshot = this.snapshot || this.data;
15202 this.data = this.queryBy(fn, scope||this);
15203 this.fireEvent("datachanged", this);
15207 * Query the records by a specified property.
15208 * @param {String} field A field on your records
15209 * @param {String/RegExp} value Either a string that the field
15210 * should start with or a RegExp to test against the field
15211 * @param {Boolean} anyMatch True to match any part not just the beginning
15212 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15214 query : function(property, value, anyMatch){
15215 var fn = this.createFilterFn(property, value, anyMatch);
15216 return fn ? this.queryBy(fn) : this.data.clone();
15220 * Query by a function. The specified function will be called with each
15221 * record in this data source. If the function returns true the record is included
15223 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15224 * @param {Object} scope (optional) The scope of the function (defaults to this)
15225 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15227 queryBy : function(fn, scope){
15228 var data = this.snapshot || this.data;
15229 return data.filterBy(fn, scope||this);
15233 * Collects unique values for a particular dataIndex from this store.
15234 * @param {String} dataIndex The property to collect
15235 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15236 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15237 * @return {Array} An array of the unique values
15239 collect : function(dataIndex, allowNull, bypassFilter){
15240 var d = (bypassFilter === true && this.snapshot) ?
15241 this.snapshot.items : this.data.items;
15242 var v, sv, r = [], l = {};
15243 for(var i = 0, len = d.length; i < len; i++){
15244 v = d[i].data[dataIndex];
15246 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15255 * Revert to a view of the Record cache with no filtering applied.
15256 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15258 clearFilter : function(suppressEvent){
15259 if(this.snapshot && this.snapshot != this.data){
15260 this.data = this.snapshot;
15261 delete this.snapshot;
15262 if(suppressEvent !== true){
15263 this.fireEvent("datachanged", this);
15269 afterEdit : function(record){
15270 if(this.modified.indexOf(record) == -1){
15271 this.modified.push(record);
15273 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15277 afterReject : function(record){
15278 this.modified.remove(record);
15279 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15283 afterCommit : function(record){
15284 this.modified.remove(record);
15285 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15289 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15290 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15292 commitChanges : function(){
15293 var m = this.modified.slice(0);
15294 this.modified = [];
15295 for(var i = 0, len = m.length; i < len; i++){
15301 * Cancel outstanding changes on all changed records.
15303 rejectChanges : function(){
15304 var m = this.modified.slice(0);
15305 this.modified = [];
15306 for(var i = 0, len = m.length; i < len; i++){
15311 onMetaChange : function(meta, rtype, o){
15312 this.recordType = rtype;
15313 this.fields = rtype.prototype.fields;
15314 delete this.snapshot;
15315 this.sortInfo = meta.sortInfo || this.sortInfo;
15316 this.modified = [];
15317 this.fireEvent('metachange', this, this.reader.meta);
15320 moveIndex : function(data, type)
15322 var index = this.indexOf(data);
15324 var newIndex = index + type;
15328 this.insert(newIndex, data);
15333 * Ext JS Library 1.1.1
15334 * Copyright(c) 2006-2007, Ext JS, LLC.
15336 * Originally Released Under LGPL - original licence link has changed is not relivant.
15339 * <script type="text/javascript">
15343 * @class Roo.data.SimpleStore
15344 * @extends Roo.data.Store
15345 * Small helper class to make creating Stores from Array data easier.
15346 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15347 * @cfg {Array} fields An array of field definition objects, or field name strings.
15348 * @cfg {Object} an existing reader (eg. copied from another store)
15349 * @cfg {Array} data The multi-dimensional array of data
15350 * @cfg {Roo.data.DataProxy} proxy [not-required]
15351 * @cfg {Roo.data.Reader} reader [not-required]
15353 * @param {Object} config
15355 Roo.data.SimpleStore = function(config)
15357 Roo.data.SimpleStore.superclass.constructor.call(this, {
15359 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15362 Roo.data.Record.create(config.fields)
15364 proxy : new Roo.data.MemoryProxy(config.data)
15368 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15370 * Ext JS Library 1.1.1
15371 * Copyright(c) 2006-2007, Ext JS, LLC.
15373 * Originally Released Under LGPL - original licence link has changed is not relivant.
15376 * <script type="text/javascript">
15381 * @extends Roo.data.Store
15382 * @class Roo.data.JsonStore
15383 * Small helper class to make creating Stores for JSON data easier. <br/>
15385 var store = new Roo.data.JsonStore({
15386 url: 'get-images.php',
15388 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15391 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15392 * JsonReader and HttpProxy (unless inline data is provided).</b>
15393 * @cfg {Array} fields An array of field definition objects, or field name strings.
15395 * @param {Object} config
15397 Roo.data.JsonStore = function(c){
15398 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15399 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15400 reader: new Roo.data.JsonReader(c, c.fields)
15403 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15405 * Ext JS Library 1.1.1
15406 * Copyright(c) 2006-2007, Ext JS, LLC.
15408 * Originally Released Under LGPL - original licence link has changed is not relivant.
15411 * <script type="text/javascript">
15415 Roo.data.Field = function(config){
15416 if(typeof config == "string"){
15417 config = {name: config};
15419 Roo.apply(this, config);
15422 this.type = "auto";
15425 var st = Roo.data.SortTypes;
15426 // named sortTypes are supported, here we look them up
15427 if(typeof this.sortType == "string"){
15428 this.sortType = st[this.sortType];
15431 // set default sortType for strings and dates
15432 if(!this.sortType){
15435 this.sortType = st.asUCString;
15438 this.sortType = st.asDate;
15441 this.sortType = st.none;
15446 var stripRe = /[\$,%]/g;
15448 // prebuilt conversion function for this field, instead of
15449 // switching every time we're reading a value
15451 var cv, dateFormat = this.dateFormat;
15456 cv = function(v){ return v; };
15459 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15463 return v !== undefined && v !== null && v !== '' ?
15464 parseInt(String(v).replace(stripRe, ""), 10) : '';
15469 return v !== undefined && v !== null && v !== '' ?
15470 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15475 cv = function(v){ return v === true || v === "true" || v == 1; };
15482 if(v instanceof Date){
15486 if(dateFormat == "timestamp"){
15487 return new Date(v*1000);
15489 return Date.parseDate(v, dateFormat);
15491 var parsed = Date.parse(v);
15492 return parsed ? new Date(parsed) : null;
15501 Roo.data.Field.prototype = {
15509 * Ext JS Library 1.1.1
15510 * Copyright(c) 2006-2007, Ext JS, LLC.
15512 * Originally Released Under LGPL - original licence link has changed is not relivant.
15515 * <script type="text/javascript">
15518 // Base class for reading structured data from a data source. This class is intended to be
15519 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15522 * @class Roo.data.DataReader
15524 * Base class for reading structured data from a data source. This class is intended to be
15525 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15528 Roo.data.DataReader = function(meta, recordType){
15532 this.recordType = recordType instanceof Array ?
15533 Roo.data.Record.create(recordType) : recordType;
15536 Roo.data.DataReader.prototype = {
15539 readerType : 'Data',
15541 * Create an empty record
15542 * @param {Object} data (optional) - overlay some values
15543 * @return {Roo.data.Record} record created.
15545 newRow : function(d) {
15547 this.recordType.prototype.fields.each(function(c) {
15549 case 'int' : da[c.name] = 0; break;
15550 case 'date' : da[c.name] = new Date(); break;
15551 case 'float' : da[c.name] = 0.0; break;
15552 case 'boolean' : da[c.name] = false; break;
15553 default : da[c.name] = ""; break;
15557 return new this.recordType(Roo.apply(da, d));
15563 * Ext JS Library 1.1.1
15564 * Copyright(c) 2006-2007, Ext JS, LLC.
15566 * Originally Released Under LGPL - original licence link has changed is not relivant.
15569 * <script type="text/javascript">
15573 * @class Roo.data.DataProxy
15574 * @extends Roo.data.Observable
15576 * This class is an abstract base class for implementations which provide retrieval of
15577 * unformatted data objects.<br>
15579 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15580 * (of the appropriate type which knows how to parse the data object) to provide a block of
15581 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15583 * Custom implementations must implement the load method as described in
15584 * {@link Roo.data.HttpProxy#load}.
15586 Roo.data.DataProxy = function(){
15589 * @event beforeload
15590 * Fires before a network request is made to retrieve a data object.
15591 * @param {Object} This DataProxy object.
15592 * @param {Object} params The params parameter to the load function.
15597 * Fires before the load method's callback is called.
15598 * @param {Object} This DataProxy object.
15599 * @param {Object} o The data object.
15600 * @param {Object} arg The callback argument object passed to the load function.
15604 * @event loadexception
15605 * Fires if an Exception occurs during data retrieval.
15606 * @param {Object} This DataProxy object.
15607 * @param {Object} o The data object.
15608 * @param {Object} arg The callback argument object passed to the load function.
15609 * @param {Object} e The Exception.
15611 loadexception : true
15613 Roo.data.DataProxy.superclass.constructor.call(this);
15616 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15619 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15623 * Ext JS Library 1.1.1
15624 * Copyright(c) 2006-2007, Ext JS, LLC.
15626 * Originally Released Under LGPL - original licence link has changed is not relivant.
15629 * <script type="text/javascript">
15632 * @class Roo.data.MemoryProxy
15633 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15634 * to the Reader when its load method is called.
15636 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15638 Roo.data.MemoryProxy = function(data){
15642 Roo.data.MemoryProxy.superclass.constructor.call(this);
15646 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15649 * Load data from the requested source (in this case an in-memory
15650 * data object passed to the constructor), read the data object into
15651 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15652 * process that block using the passed callback.
15653 * @param {Object} params This parameter is not used by the MemoryProxy class.
15654 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15655 * object into a block of Roo.data.Records.
15656 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15657 * The function must be passed <ul>
15658 * <li>The Record block object</li>
15659 * <li>The "arg" argument from the load function</li>
15660 * <li>A boolean success indicator</li>
15662 * @param {Object} scope The scope in which to call the callback
15663 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15665 load : function(params, reader, callback, scope, arg){
15666 params = params || {};
15669 result = reader.readRecords(params.data ? params.data :this.data);
15671 this.fireEvent("loadexception", this, arg, null, e);
15672 callback.call(scope, null, arg, false);
15675 callback.call(scope, result, arg, true);
15679 update : function(params, records){
15684 * Ext JS Library 1.1.1
15685 * Copyright(c) 2006-2007, Ext JS, LLC.
15687 * Originally Released Under LGPL - original licence link has changed is not relivant.
15690 * <script type="text/javascript">
15693 * @class Roo.data.HttpProxy
15694 * @extends Roo.data.DataProxy
15695 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15696 * configured to reference a certain URL.<br><br>
15698 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15699 * from which the running page was served.<br><br>
15701 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15703 * Be aware that to enable the browser to parse an XML document, the server must set
15704 * the Content-Type header in the HTTP response to "text/xml".
15706 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15707 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15708 * will be used to make the request.
15710 Roo.data.HttpProxy = function(conn){
15711 Roo.data.HttpProxy.superclass.constructor.call(this);
15712 // is conn a conn config or a real conn?
15714 this.useAjax = !conn || !conn.events;
15718 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15719 // thse are take from connection...
15722 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15725 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15726 * extra parameters to each request made by this object. (defaults to undefined)
15729 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15730 * to each request made by this object. (defaults to undefined)
15733 * @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)
15736 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15739 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15745 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15749 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15750 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15751 * a finer-grained basis than the DataProxy events.
15753 getConnection : function(){
15754 return this.useAjax ? Roo.Ajax : this.conn;
15758 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15759 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15760 * process that block using the passed callback.
15761 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15762 * for the request to the remote server.
15763 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15764 * object into a block of Roo.data.Records.
15765 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15766 * The function must be passed <ul>
15767 * <li>The Record block object</li>
15768 * <li>The "arg" argument from the load function</li>
15769 * <li>A boolean success indicator</li>
15771 * @param {Object} scope The scope in which to call the callback
15772 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15774 load : function(params, reader, callback, scope, arg){
15775 if(this.fireEvent("beforeload", this, params) !== false){
15777 params : params || {},
15779 callback : callback,
15784 callback : this.loadResponse,
15788 Roo.applyIf(o, this.conn);
15789 if(this.activeRequest){
15790 Roo.Ajax.abort(this.activeRequest);
15792 this.activeRequest = Roo.Ajax.request(o);
15794 this.conn.request(o);
15797 callback.call(scope||this, null, arg, false);
15802 loadResponse : function(o, success, response){
15803 delete this.activeRequest;
15805 this.fireEvent("loadexception", this, o, response);
15806 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15811 result = o.reader.read(response);
15813 this.fireEvent("loadexception", this, o, response, e);
15814 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15818 this.fireEvent("load", this, o, o.request.arg);
15819 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15823 update : function(dataSet){
15828 updateResponse : function(dataSet){
15833 * Ext JS Library 1.1.1
15834 * Copyright(c) 2006-2007, Ext JS, LLC.
15836 * Originally Released Under LGPL - original licence link has changed is not relivant.
15839 * <script type="text/javascript">
15843 * @class Roo.data.ScriptTagProxy
15844 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15845 * other than the originating domain of the running page.<br><br>
15847 * <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
15848 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15850 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15851 * source code that is used as the source inside a <script> tag.<br><br>
15853 * In order for the browser to process the returned data, the server must wrap the data object
15854 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15855 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15856 * depending on whether the callback name was passed:
15859 boolean scriptTag = false;
15860 String cb = request.getParameter("callback");
15863 response.setContentType("text/javascript");
15865 response.setContentType("application/x-json");
15867 Writer out = response.getWriter();
15869 out.write(cb + "(");
15871 out.print(dataBlock.toJsonString());
15878 * @param {Object} config A configuration object.
15880 Roo.data.ScriptTagProxy = function(config){
15881 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15882 Roo.apply(this, config);
15883 this.head = document.getElementsByTagName("head")[0];
15886 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15888 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15890 * @cfg {String} url The URL from which to request the data object.
15893 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15897 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15898 * the server the name of the callback function set up by the load call to process the returned data object.
15899 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15900 * javascript output which calls this named function passing the data object as its only parameter.
15902 callbackParam : "callback",
15904 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15905 * name to the request.
15910 * Load data from the configured URL, read the data object into
15911 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15912 * process that block using the passed callback.
15913 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15914 * for the request to the remote server.
15915 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15916 * object into a block of Roo.data.Records.
15917 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15918 * The function must be passed <ul>
15919 * <li>The Record block object</li>
15920 * <li>The "arg" argument from the load function</li>
15921 * <li>A boolean success indicator</li>
15923 * @param {Object} scope The scope in which to call the callback
15924 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15926 load : function(params, reader, callback, scope, arg){
15927 if(this.fireEvent("beforeload", this, params) !== false){
15929 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15931 var url = this.url;
15932 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15934 url += "&_dc=" + (new Date().getTime());
15936 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15939 cb : "stcCallback"+transId,
15940 scriptId : "stcScript"+transId,
15944 callback : callback,
15950 window[trans.cb] = function(o){
15951 conn.handleResponse(o, trans);
15954 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15956 if(this.autoAbort !== false){
15960 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15962 var script = document.createElement("script");
15963 script.setAttribute("src", url);
15964 script.setAttribute("type", "text/javascript");
15965 script.setAttribute("id", trans.scriptId);
15966 this.head.appendChild(script);
15968 this.trans = trans;
15970 callback.call(scope||this, null, arg, false);
15975 isLoading : function(){
15976 return this.trans ? true : false;
15980 * Abort the current server request.
15982 abort : function(){
15983 if(this.isLoading()){
15984 this.destroyTrans(this.trans);
15989 destroyTrans : function(trans, isLoaded){
15990 this.head.removeChild(document.getElementById(trans.scriptId));
15991 clearTimeout(trans.timeoutId);
15993 window[trans.cb] = undefined;
15995 delete window[trans.cb];
15998 // if hasn't been loaded, wait for load to remove it to prevent script error
15999 window[trans.cb] = function(){
16000 window[trans.cb] = undefined;
16002 delete window[trans.cb];
16009 handleResponse : function(o, trans){
16010 this.trans = false;
16011 this.destroyTrans(trans, true);
16014 result = trans.reader.readRecords(o);
16016 this.fireEvent("loadexception", this, o, trans.arg, e);
16017 trans.callback.call(trans.scope||window, null, trans.arg, false);
16020 this.fireEvent("load", this, o, trans.arg);
16021 trans.callback.call(trans.scope||window, result, trans.arg, true);
16025 handleFailure : function(trans){
16026 this.trans = false;
16027 this.destroyTrans(trans, false);
16028 this.fireEvent("loadexception", this, null, trans.arg);
16029 trans.callback.call(trans.scope||window, null, trans.arg, false);
16033 * Ext JS Library 1.1.1
16034 * Copyright(c) 2006-2007, Ext JS, LLC.
16036 * Originally Released Under LGPL - original licence link has changed is not relivant.
16039 * <script type="text/javascript">
16043 * @class Roo.data.JsonReader
16044 * @extends Roo.data.DataReader
16045 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16046 * based on mappings in a provided Roo.data.Record constructor.
16048 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16049 * in the reply previously.
16054 var RecordDef = Roo.data.Record.create([
16055 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16056 {name: 'occupation'} // This field will use "occupation" as the mapping.
16058 var myReader = new Roo.data.JsonReader({
16059 totalProperty: "results", // The property which contains the total dataset size (optional)
16060 root: "rows", // The property which contains an Array of row objects
16061 id: "id" // The property within each row object that provides an ID for the record (optional)
16065 * This would consume a JSON file like this:
16067 { 'results': 2, 'rows': [
16068 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16069 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16072 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16073 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16074 * paged from the remote server.
16075 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16076 * @cfg {String} root name of the property which contains the Array of row objects.
16077 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16078 * @cfg {Array} fields Array of field definition objects
16080 * Create a new JsonReader
16081 * @param {Object} meta Metadata configuration options
16082 * @param {Object} recordType Either an Array of field definition objects,
16083 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16085 Roo.data.JsonReader = function(meta, recordType){
16088 // set some defaults:
16089 Roo.applyIf(meta, {
16090 totalProperty: 'total',
16091 successProperty : 'success',
16096 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16098 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16100 readerType : 'Json',
16103 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16104 * Used by Store query builder to append _requestMeta to params.
16107 metaFromRemote : false,
16109 * This method is only used by a DataProxy which has retrieved data from a remote server.
16110 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16111 * @return {Object} data A data block which is used by an Roo.data.Store object as
16112 * a cache of Roo.data.Records.
16114 read : function(response){
16115 var json = response.responseText;
16117 var o = /* eval:var:o */ eval("("+json+")");
16119 throw {message: "JsonReader.read: Json object not found"};
16125 this.metaFromRemote = true;
16126 this.meta = o.metaData;
16127 this.recordType = Roo.data.Record.create(o.metaData.fields);
16128 this.onMetaChange(this.meta, this.recordType, o);
16130 return this.readRecords(o);
16133 // private function a store will implement
16134 onMetaChange : function(meta, recordType, o){
16141 simpleAccess: function(obj, subsc) {
16148 getJsonAccessor: function(){
16150 return function(expr) {
16152 return(re.test(expr))
16153 ? new Function("obj", "return obj." + expr)
16158 return Roo.emptyFn;
16163 * Create a data block containing Roo.data.Records from an XML document.
16164 * @param {Object} o An object which contains an Array of row objects in the property specified
16165 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16166 * which contains the total size of the dataset.
16167 * @return {Object} data A data block which is used by an Roo.data.Store object as
16168 * a cache of Roo.data.Records.
16170 readRecords : function(o){
16172 * After any data loads, the raw JSON data is available for further custom processing.
16176 var s = this.meta, Record = this.recordType,
16177 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16179 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16181 if(s.totalProperty) {
16182 this.getTotal = this.getJsonAccessor(s.totalProperty);
16184 if(s.successProperty) {
16185 this.getSuccess = this.getJsonAccessor(s.successProperty);
16187 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16189 var g = this.getJsonAccessor(s.id);
16190 this.getId = function(rec) {
16192 return (r === undefined || r === "") ? null : r;
16195 this.getId = function(){return null;};
16198 for(var jj = 0; jj < fl; jj++){
16200 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16201 this.ef[jj] = this.getJsonAccessor(map);
16205 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16206 if(s.totalProperty){
16207 var vt = parseInt(this.getTotal(o), 10);
16212 if(s.successProperty){
16213 var vs = this.getSuccess(o);
16214 if(vs === false || vs === 'false'){
16219 for(var i = 0; i < c; i++){
16222 var id = this.getId(n);
16223 for(var j = 0; j < fl; j++){
16225 var v = this.ef[j](n);
16227 Roo.log('missing convert for ' + f.name);
16231 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16233 var record = new Record(values, id);
16235 records[i] = record;
16241 totalRecords : totalRecords
16244 // used when loading children.. @see loadDataFromChildren
16245 toLoadData: function(rec)
16247 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16248 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16249 return { data : data, total : data.length };
16254 * Ext JS Library 1.1.1
16255 * Copyright(c) 2006-2007, Ext JS, LLC.
16257 * Originally Released Under LGPL - original licence link has changed is not relivant.
16260 * <script type="text/javascript">
16264 * @class Roo.data.ArrayReader
16265 * @extends Roo.data.DataReader
16266 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16267 * Each element of that Array represents a row of data fields. The
16268 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16269 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16273 var RecordDef = Roo.data.Record.create([
16274 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16275 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16277 var myReader = new Roo.data.ArrayReader({
16278 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16282 * This would consume an Array like this:
16284 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16288 * Create a new JsonReader
16289 * @param {Object} meta Metadata configuration options.
16290 * @param {Object|Array} recordType Either an Array of field definition objects
16292 * @cfg {Array} fields Array of field definition objects
16293 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16294 * as specified to {@link Roo.data.Record#create},
16295 * or an {@link Roo.data.Record} object
16298 * created using {@link Roo.data.Record#create}.
16300 Roo.data.ArrayReader = function(meta, recordType)
16302 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16305 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16308 * Create a data block containing Roo.data.Records from an XML document.
16309 * @param {Object} o An Array of row objects which represents the dataset.
16310 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16311 * a cache of Roo.data.Records.
16313 readRecords : function(o)
16315 var sid = this.meta ? this.meta.id : null;
16316 var recordType = this.recordType, fields = recordType.prototype.fields;
16319 for(var i = 0; i < root.length; i++){
16322 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16323 for(var j = 0, jlen = fields.length; j < jlen; j++){
16324 var f = fields.items[j];
16325 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16326 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16328 values[f.name] = v;
16330 var record = new recordType(values, id);
16332 records[records.length] = record;
16336 totalRecords : records.length
16339 // used when loading children.. @see loadDataFromChildren
16340 toLoadData: function(rec)
16342 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16343 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16354 * @class Roo.bootstrap.ComboBox
16355 * @extends Roo.bootstrap.TriggerField
16356 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16357 * @cfg {Boolean} append (true|false) default false
16358 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16359 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16360 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16361 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16362 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16363 * @cfg {Boolean} animate default true
16364 * @cfg {Boolean} emptyResultText only for touch device
16365 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16366 * @cfg {String} emptyTitle default ''
16367 * @cfg {Number} width fixed with? experimental
16369 * Create a new ComboBox.
16370 * @param {Object} config Configuration options
16372 Roo.bootstrap.ComboBox = function(config){
16373 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16377 * Fires when the dropdown list is expanded
16378 * @param {Roo.bootstrap.ComboBox} combo This combo box
16383 * Fires when the dropdown list is collapsed
16384 * @param {Roo.bootstrap.ComboBox} combo This combo box
16388 * @event beforeselect
16389 * Fires before a list item is selected. Return false to cancel the selection.
16390 * @param {Roo.bootstrap.ComboBox} combo This combo box
16391 * @param {Roo.data.Record} record The data record returned from the underlying store
16392 * @param {Number} index The index of the selected item in the dropdown list
16394 'beforeselect' : true,
16397 * Fires when a list item is selected
16398 * @param {Roo.bootstrap.ComboBox} combo This combo box
16399 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16400 * @param {Number} index The index of the selected item in the dropdown list
16404 * @event beforequery
16405 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16406 * The event object passed has these properties:
16407 * @param {Roo.bootstrap.ComboBox} combo This combo box
16408 * @param {String} query The query
16409 * @param {Boolean} forceAll true to force "all" query
16410 * @param {Boolean} cancel true to cancel the query
16411 * @param {Object} e The query event object
16413 'beforequery': true,
16416 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16417 * @param {Roo.bootstrap.ComboBox} combo This combo box
16422 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16423 * @param {Roo.bootstrap.ComboBox} combo This combo box
16424 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16429 * Fires when the remove value from the combobox array
16430 * @param {Roo.bootstrap.ComboBox} combo This combo box
16434 * @event afterremove
16435 * Fires when the remove value from the combobox array
16436 * @param {Roo.bootstrap.ComboBox} combo This combo box
16438 'afterremove' : true,
16440 * @event specialfilter
16441 * Fires when specialfilter
16442 * @param {Roo.bootstrap.ComboBox} combo This combo box
16444 'specialfilter' : true,
16447 * Fires when tick the element
16448 * @param {Roo.bootstrap.ComboBox} combo This combo box
16452 * @event touchviewdisplay
16453 * Fires when touch view require special display (default is using displayField)
16454 * @param {Roo.bootstrap.ComboBox} combo This combo box
16455 * @param {Object} cfg set html .
16457 'touchviewdisplay' : true
16462 this.tickItems = [];
16464 this.selectedIndex = -1;
16465 if(this.mode == 'local'){
16466 if(config.queryDelay === undefined){
16467 this.queryDelay = 10;
16469 if(config.minChars === undefined){
16475 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16478 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16479 * rendering into an Roo.Editor, defaults to false)
16482 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16483 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16486 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16489 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16490 * the dropdown list (defaults to undefined, with no header element)
16494 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16498 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16500 listWidth: undefined,
16502 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16503 * mode = 'remote' or 'text' if mode = 'local')
16505 displayField: undefined,
16508 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16509 * mode = 'remote' or 'value' if mode = 'local').
16510 * Note: use of a valueField requires the user make a selection
16511 * in order for a value to be mapped.
16513 valueField: undefined,
16515 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16520 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16521 * field's data value (defaults to the underlying DOM element's name)
16523 hiddenName: undefined,
16525 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16529 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16531 selectedClass: 'active',
16534 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16538 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16539 * anchor positions (defaults to 'tl-bl')
16541 listAlign: 'tl-bl?',
16543 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16547 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16548 * query specified by the allQuery config option (defaults to 'query')
16550 triggerAction: 'query',
16552 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16553 * (defaults to 4, does not apply if editable = false)
16557 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16558 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16562 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16563 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16567 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16568 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16572 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16573 * when editable = true (defaults to false)
16575 selectOnFocus:false,
16577 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16579 queryParam: 'query',
16581 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16582 * when mode = 'remote' (defaults to 'Loading...')
16584 loadingText: 'Loading...',
16586 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16590 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16594 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16595 * traditional select (defaults to true)
16599 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16603 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16607 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16608 * listWidth has a higher value)
16612 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16613 * allow the user to set arbitrary text into the field (defaults to false)
16615 forceSelection:false,
16617 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16618 * if typeAhead = true (defaults to 250)
16620 typeAheadDelay : 250,
16622 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16623 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16625 valueNotFoundText : undefined,
16627 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16629 blockFocus : false,
16632 * @cfg {Boolean} disableClear Disable showing of clear button.
16634 disableClear : false,
16636 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16638 alwaysQuery : false,
16641 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16646 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16648 invalidClass : "has-warning",
16651 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16653 validClass : "has-success",
16656 * @cfg {Boolean} specialFilter (true|false) special filter default false
16658 specialFilter : false,
16661 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16663 mobileTouchView : true,
16666 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16668 useNativeIOS : false,
16671 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16673 mobile_restrict_height : false,
16675 ios_options : false,
16687 btnPosition : 'right',
16688 triggerList : true,
16689 showToggleBtn : true,
16691 emptyResultText: 'Empty',
16692 triggerText : 'Select',
16696 // element that contains real text value.. (when hidden is used..)
16698 getAutoCreate : function()
16703 * Render classic select for iso
16706 if(Roo.isIOS && this.useNativeIOS){
16707 cfg = this.getAutoCreateNativeIOS();
16715 if(Roo.isTouch && this.mobileTouchView){
16716 cfg = this.getAutoCreateTouchView();
16723 if(!this.tickable){
16724 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16729 * ComboBox with tickable selections
16732 var align = this.labelAlign || this.parentLabelAlign();
16735 cls : 'form-group roo-combobox-tickable' //input-group
16738 var btn_text_select = '';
16739 var btn_text_done = '';
16740 var btn_text_cancel = '';
16742 if (this.btn_text_show) {
16743 btn_text_select = 'Select';
16744 btn_text_done = 'Done';
16745 btn_text_cancel = 'Cancel';
16750 cls : 'tickable-buttons',
16755 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16756 //html : this.triggerText
16757 html: btn_text_select
16763 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16765 html: btn_text_done
16771 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16773 html: btn_text_cancel
16779 buttons.cn.unshift({
16781 cls: 'roo-select2-search-field-input'
16787 Roo.each(buttons.cn, function(c){
16789 c.cls += ' btn-' + _this.size;
16792 if (_this.disabled) {
16799 style : 'display: contents',
16804 cls: 'form-hidden-field'
16808 cls: 'roo-select2-choices',
16812 cls: 'roo-select2-search-field',
16823 cls: 'roo-select2-container input-group roo-select2-container-multi',
16829 // cls: 'typeahead typeahead-long dropdown-menu',
16830 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16835 if(this.hasFeedback && !this.allowBlank){
16839 cls: 'glyphicon form-control-feedback'
16842 combobox.cn.push(feedback);
16849 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16850 tooltip : 'This field is required'
16852 if (Roo.bootstrap.version == 4) {
16855 style : 'display:none'
16858 if (align ==='left' && this.fieldLabel.length) {
16860 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16867 cls : 'control-label col-form-label',
16868 html : this.fieldLabel
16880 var labelCfg = cfg.cn[1];
16881 var contentCfg = cfg.cn[2];
16884 if(this.indicatorpos == 'right'){
16890 cls : 'control-label col-form-label',
16894 html : this.fieldLabel
16910 labelCfg = cfg.cn[0];
16911 contentCfg = cfg.cn[1];
16915 if(this.labelWidth > 12){
16916 labelCfg.style = "width: " + this.labelWidth + 'px';
16918 if(this.width * 1 > 0){
16919 contentCfg.style = "width: " + this.width + 'px';
16921 if(this.labelWidth < 13 && this.labelmd == 0){
16922 this.labelmd = this.labelWidth;
16925 if(this.labellg > 0){
16926 labelCfg.cls += ' col-lg-' + this.labellg;
16927 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16930 if(this.labelmd > 0){
16931 labelCfg.cls += ' col-md-' + this.labelmd;
16932 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16935 if(this.labelsm > 0){
16936 labelCfg.cls += ' col-sm-' + this.labelsm;
16937 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16940 if(this.labelxs > 0){
16941 labelCfg.cls += ' col-xs-' + this.labelxs;
16942 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16946 } else if ( this.fieldLabel.length) {
16947 // Roo.log(" label");
16952 //cls : 'input-group-addon',
16953 html : this.fieldLabel
16958 if(this.indicatorpos == 'right'){
16962 //cls : 'input-group-addon',
16963 html : this.fieldLabel
16973 // Roo.log(" no label && no align");
16980 ['xs','sm','md','lg'].map(function(size){
16981 if (settings[size]) {
16982 cfg.cls += ' col-' + size + '-' + settings[size];
16990 _initEventsCalled : false,
16993 initEvents: function()
16995 if (this._initEventsCalled) { // as we call render... prevent looping...
16998 this._initEventsCalled = true;
17001 throw "can not find store for combo";
17004 this.indicator = this.indicatorEl();
17006 this.store = Roo.factory(this.store, Roo.data);
17007 this.store.parent = this;
17009 // if we are building from html. then this element is so complex, that we can not really
17010 // use the rendered HTML.
17011 // so we have to trash and replace the previous code.
17012 if (Roo.XComponent.build_from_html) {
17013 // remove this element....
17014 var e = this.el.dom, k=0;
17015 while (e ) { e = e.previousSibling; ++k;}
17020 this.rendered = false;
17022 this.render(this.parent().getChildContainer(true), k);
17025 if(Roo.isIOS && this.useNativeIOS){
17026 this.initIOSView();
17034 if(Roo.isTouch && this.mobileTouchView){
17035 this.initTouchView();
17040 this.initTickableEvents();
17044 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17046 if(this.hiddenName){
17048 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17050 this.hiddenField.dom.value =
17051 this.hiddenValue !== undefined ? this.hiddenValue :
17052 this.value !== undefined ? this.value : '';
17054 // prevent input submission
17055 this.el.dom.removeAttribute('name');
17056 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17061 // this.el.dom.setAttribute('autocomplete', 'off');
17064 var cls = 'x-combo-list';
17066 //this.list = new Roo.Layer({
17067 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17073 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17074 _this.list.setWidth(lw);
17077 this.list.on('mouseover', this.onViewOver, this);
17078 this.list.on('mousemove', this.onViewMove, this);
17079 this.list.on('scroll', this.onViewScroll, this);
17082 this.list.swallowEvent('mousewheel');
17083 this.assetHeight = 0;
17086 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17087 this.assetHeight += this.header.getHeight();
17090 this.innerList = this.list.createChild({cls:cls+'-inner'});
17091 this.innerList.on('mouseover', this.onViewOver, this);
17092 this.innerList.on('mousemove', this.onViewMove, this);
17093 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17095 if(this.allowBlank && !this.pageSize && !this.disableClear){
17096 this.footer = this.list.createChild({cls:cls+'-ft'});
17097 this.pageTb = new Roo.Toolbar(this.footer);
17101 this.footer = this.list.createChild({cls:cls+'-ft'});
17102 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17103 {pageSize: this.pageSize});
17107 if (this.pageTb && this.allowBlank && !this.disableClear) {
17109 this.pageTb.add(new Roo.Toolbar.Fill(), {
17110 cls: 'x-btn-icon x-btn-clear',
17112 handler: function()
17115 _this.clearValue();
17116 _this.onSelect(false, -1);
17121 this.assetHeight += this.footer.getHeight();
17126 this.tpl = Roo.bootstrap.version == 4 ?
17127 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17128 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17131 this.view = new Roo.View(this.list, this.tpl, {
17132 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17134 //this.view.wrapEl.setDisplayed(false);
17135 this.view.on('click', this.onViewClick, this);
17138 this.store.on('beforeload', this.onBeforeLoad, this);
17139 this.store.on('load', this.onLoad, this);
17140 this.store.on('loadexception', this.onLoadException, this);
17142 if(this.resizable){
17143 this.resizer = new Roo.Resizable(this.list, {
17144 pinned:true, handles:'se'
17146 this.resizer.on('resize', function(r, w, h){
17147 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17148 this.listWidth = w;
17149 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17150 this.restrictHeight();
17152 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17155 if(!this.editable){
17156 this.editable = true;
17157 this.setEditable(false);
17162 if (typeof(this.events.add.listeners) != 'undefined') {
17164 this.addicon = this.wrap.createChild(
17165 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17167 this.addicon.on('click', function(e) {
17168 this.fireEvent('add', this);
17171 if (typeof(this.events.edit.listeners) != 'undefined') {
17173 this.editicon = this.wrap.createChild(
17174 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17175 if (this.addicon) {
17176 this.editicon.setStyle('margin-left', '40px');
17178 this.editicon.on('click', function(e) {
17180 // we fire even if inothing is selected..
17181 this.fireEvent('edit', this, this.lastData );
17187 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17188 "up" : function(e){
17189 this.inKeyMode = true;
17193 "down" : function(e){
17194 if(!this.isExpanded()){
17195 this.onTriggerClick();
17197 this.inKeyMode = true;
17202 "enter" : function(e){
17203 // this.onViewClick();
17207 if(this.fireEvent("specialkey", this, e)){
17208 this.onViewClick(false);
17214 "esc" : function(e){
17218 "tab" : function(e){
17221 if(this.fireEvent("specialkey", this, e)){
17222 this.onViewClick(false);
17230 doRelay : function(foo, bar, hname){
17231 if(hname == 'down' || this.scope.isExpanded()){
17232 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17241 this.queryDelay = Math.max(this.queryDelay || 10,
17242 this.mode == 'local' ? 10 : 250);
17245 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17247 if(this.typeAhead){
17248 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17250 if(this.editable !== false){
17251 this.inputEl().on("keyup", this.onKeyUp, this);
17253 if(this.forceSelection){
17254 this.inputEl().on('blur', this.doForce, this);
17258 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17259 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17263 initTickableEvents: function()
17267 if(this.hiddenName){
17269 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17271 this.hiddenField.dom.value =
17272 this.hiddenValue !== undefined ? this.hiddenValue :
17273 this.value !== undefined ? this.value : '';
17275 // prevent input submission
17276 this.el.dom.removeAttribute('name');
17277 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17282 // this.list = this.el.select('ul.dropdown-menu',true).first();
17284 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17285 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17286 if(this.triggerList){
17287 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17290 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17291 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17293 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17294 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17296 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17297 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17299 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17300 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17301 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17304 this.cancelBtn.hide();
17309 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17310 _this.list.setWidth(lw);
17313 this.list.on('mouseover', this.onViewOver, this);
17314 this.list.on('mousemove', this.onViewMove, this);
17316 this.list.on('scroll', this.onViewScroll, this);
17319 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17320 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17323 this.view = new Roo.View(this.list, this.tpl, {
17328 selectedClass: this.selectedClass
17331 //this.view.wrapEl.setDisplayed(false);
17332 this.view.on('click', this.onViewClick, this);
17336 this.store.on('beforeload', this.onBeforeLoad, this);
17337 this.store.on('load', this.onLoad, this);
17338 this.store.on('loadexception', this.onLoadException, this);
17341 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17342 "up" : function(e){
17343 this.inKeyMode = true;
17347 "down" : function(e){
17348 this.inKeyMode = true;
17352 "enter" : function(e){
17353 if(this.fireEvent("specialkey", this, e)){
17354 this.onViewClick(false);
17360 "esc" : function(e){
17361 this.onTickableFooterButtonClick(e, false, false);
17364 "tab" : function(e){
17365 this.fireEvent("specialkey", this, e);
17367 this.onTickableFooterButtonClick(e, false, false);
17374 doRelay : function(e, fn, key){
17375 if(this.scope.isExpanded()){
17376 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17385 this.queryDelay = Math.max(this.queryDelay || 10,
17386 this.mode == 'local' ? 10 : 250);
17389 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17391 if(this.typeAhead){
17392 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17395 if(this.editable !== false){
17396 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17399 this.indicator = this.indicatorEl();
17401 if(this.indicator){
17402 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17403 this.indicator.hide();
17408 onDestroy : function(){
17410 this.view.setStore(null);
17411 this.view.el.removeAllListeners();
17412 this.view.el.remove();
17413 this.view.purgeListeners();
17416 this.list.dom.innerHTML = '';
17420 this.store.un('beforeload', this.onBeforeLoad, this);
17421 this.store.un('load', this.onLoad, this);
17422 this.store.un('loadexception', this.onLoadException, this);
17424 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17428 fireKey : function(e){
17429 if(e.isNavKeyPress() && !this.list.isVisible()){
17430 this.fireEvent("specialkey", this, e);
17435 onResize: function(w, h)
17439 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17441 // if(typeof w != 'number'){
17442 // // we do not handle it!?!?
17445 // var tw = this.trigger.getWidth();
17446 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17447 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17449 // this.inputEl().setWidth( this.adjustWidth('input', x));
17451 // //this.trigger.setStyle('left', x+'px');
17453 // if(this.list && this.listWidth === undefined){
17454 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17455 // this.list.setWidth(lw);
17456 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17464 * Allow or prevent the user from directly editing the field text. If false is passed,
17465 * the user will only be able to select from the items defined in the dropdown list. This method
17466 * is the runtime equivalent of setting the 'editable' config option at config time.
17467 * @param {Boolean} value True to allow the user to directly edit the field text
17469 setEditable : function(value){
17470 if(value == this.editable){
17473 this.editable = value;
17475 this.inputEl().dom.setAttribute('readOnly', true);
17476 this.inputEl().on('mousedown', this.onTriggerClick, this);
17477 this.inputEl().addClass('x-combo-noedit');
17479 this.inputEl().dom.removeAttribute('readOnly');
17480 this.inputEl().un('mousedown', this.onTriggerClick, this);
17481 this.inputEl().removeClass('x-combo-noedit');
17487 onBeforeLoad : function(combo,opts){
17488 if(!this.hasFocus){
17492 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17494 this.restrictHeight();
17495 this.selectedIndex = -1;
17499 onLoad : function(){
17501 this.hasQuery = false;
17503 if(!this.hasFocus){
17507 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17508 this.loading.hide();
17511 if(this.store.getCount() > 0){
17514 this.restrictHeight();
17515 if(this.lastQuery == this.allQuery){
17516 if(this.editable && !this.tickable){
17517 this.inputEl().dom.select();
17521 !this.selectByValue(this.value, true) &&
17524 !this.store.lastOptions ||
17525 typeof(this.store.lastOptions.add) == 'undefined' ||
17526 this.store.lastOptions.add != true
17529 this.select(0, true);
17532 if(this.autoFocus){
17535 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17536 this.taTask.delay(this.typeAheadDelay);
17540 this.onEmptyResults();
17546 onLoadException : function()
17548 this.hasQuery = false;
17550 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17551 this.loading.hide();
17554 if(this.tickable && this.editable){
17559 // only causes errors at present
17560 //Roo.log(this.store.reader.jsonData);
17561 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17563 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17569 onTypeAhead : function(){
17570 if(this.store.getCount() > 0){
17571 var r = this.store.getAt(0);
17572 var newValue = r.data[this.displayField];
17573 var len = newValue.length;
17574 var selStart = this.getRawValue().length;
17576 if(selStart != len){
17577 this.setRawValue(newValue);
17578 this.selectText(selStart, newValue.length);
17584 onSelect : function(record, index){
17586 if(this.fireEvent('beforeselect', this, record, index) !== false){
17588 this.setFromData(index > -1 ? record.data : false);
17591 this.fireEvent('select', this, record, index);
17596 * Returns the currently selected field value or empty string if no value is set.
17597 * @return {String} value The selected value
17599 getValue : function()
17601 if(Roo.isIOS && this.useNativeIOS){
17602 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17606 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17609 if(this.valueField){
17610 return typeof this.value != 'undefined' ? this.value : '';
17612 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17616 getRawValue : function()
17618 if(Roo.isIOS && this.useNativeIOS){
17619 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17622 var v = this.inputEl().getValue();
17628 * Clears any text/value currently set in the field
17630 clearValue : function(){
17632 if(this.hiddenField){
17633 this.hiddenField.dom.value = '';
17636 this.setRawValue('');
17637 this.lastSelectionText = '';
17638 this.lastData = false;
17640 var close = this.closeTriggerEl();
17651 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17652 * will be displayed in the field. If the value does not match the data value of an existing item,
17653 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17654 * Otherwise the field will be blank (although the value will still be set).
17655 * @param {String} value The value to match
17657 setValue : function(v)
17659 if(Roo.isIOS && this.useNativeIOS){
17660 this.setIOSValue(v);
17670 if(this.valueField){
17671 var r = this.findRecord(this.valueField, v);
17673 text = r.data[this.displayField];
17674 }else if(this.valueNotFoundText !== undefined){
17675 text = this.valueNotFoundText;
17678 this.lastSelectionText = text;
17679 if(this.hiddenField){
17680 this.hiddenField.dom.value = v;
17682 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17685 var close = this.closeTriggerEl();
17688 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17694 * @property {Object} the last set data for the element
17699 * Sets the value of the field based on a object which is related to the record format for the store.
17700 * @param {Object} value the value to set as. or false on reset?
17702 setFromData : function(o){
17709 var dv = ''; // display value
17710 var vv = ''; // value value..
17712 if (this.displayField) {
17713 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17715 // this is an error condition!!!
17716 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17719 if(this.valueField){
17720 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17723 var close = this.closeTriggerEl();
17726 if(dv.length || vv * 1 > 0){
17728 this.blockFocus=true;
17734 if(this.hiddenField){
17735 this.hiddenField.dom.value = vv;
17737 this.lastSelectionText = dv;
17738 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17742 // no hidden field.. - we store the value in 'value', but still display
17743 // display field!!!!
17744 this.lastSelectionText = dv;
17745 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17752 reset : function(){
17753 // overridden so that last data is reset..
17760 this.setValue(this.originalValue);
17761 //this.clearInvalid();
17762 this.lastData = false;
17764 this.view.clearSelections();
17770 findRecord : function(prop, value){
17772 if(this.store.getCount() > 0){
17773 this.store.each(function(r){
17774 if(r.data[prop] == value){
17784 getName: function()
17786 // returns hidden if it's set..
17787 if (!this.rendered) {return ''};
17788 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17792 onViewMove : function(e, t){
17793 this.inKeyMode = false;
17797 onViewOver : function(e, t){
17798 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17801 var item = this.view.findItemFromChild(t);
17804 var index = this.view.indexOf(item);
17805 this.select(index, false);
17810 onViewClick : function(view, doFocus, el, e)
17812 var index = this.view.getSelectedIndexes()[0];
17814 var r = this.store.getAt(index);
17818 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17825 Roo.each(this.tickItems, function(v,k){
17827 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17829 _this.tickItems.splice(k, 1);
17831 if(typeof(e) == 'undefined' && view == false){
17832 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17844 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17845 this.tickItems.push(r.data);
17848 if(typeof(e) == 'undefined' && view == false){
17849 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17856 this.onSelect(r, index);
17858 if(doFocus !== false && !this.blockFocus){
17859 this.inputEl().focus();
17864 restrictHeight : function(){
17865 //this.innerList.dom.style.height = '';
17866 //var inner = this.innerList.dom;
17867 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17868 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17869 //this.list.beginUpdate();
17870 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17871 this.list.alignTo(this.inputEl(), this.listAlign);
17872 this.list.alignTo(this.inputEl(), this.listAlign);
17873 //this.list.endUpdate();
17877 onEmptyResults : function(){
17879 if(this.tickable && this.editable){
17880 this.hasFocus = false;
17881 this.restrictHeight();
17889 * Returns true if the dropdown list is expanded, else false.
17891 isExpanded : function(){
17892 return this.list.isVisible();
17896 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17897 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17898 * @param {String} value The data value of the item to select
17899 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17900 * selected item if it is not currently in view (defaults to true)
17901 * @return {Boolean} True if the value matched an item in the list, else false
17903 selectByValue : function(v, scrollIntoView){
17904 if(v !== undefined && v !== null){
17905 var r = this.findRecord(this.valueField || this.displayField, v);
17907 this.select(this.store.indexOf(r), scrollIntoView);
17915 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17916 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17917 * @param {Number} index The zero-based index of the list item to select
17918 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17919 * selected item if it is not currently in view (defaults to true)
17921 select : function(index, scrollIntoView){
17922 this.selectedIndex = index;
17923 this.view.select(index);
17924 if(scrollIntoView !== false){
17925 var el = this.view.getNode(index);
17927 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17930 this.list.scrollChildIntoView(el, false);
17936 selectNext : function(){
17937 var ct = this.store.getCount();
17939 if(this.selectedIndex == -1){
17941 }else if(this.selectedIndex < ct-1){
17942 this.select(this.selectedIndex+1);
17948 selectPrev : function(){
17949 var ct = this.store.getCount();
17951 if(this.selectedIndex == -1){
17953 }else if(this.selectedIndex != 0){
17954 this.select(this.selectedIndex-1);
17960 onKeyUp : function(e){
17961 if(this.editable !== false && !e.isSpecialKey()){
17962 this.lastKey = e.getKey();
17963 this.dqTask.delay(this.queryDelay);
17968 validateBlur : function(){
17969 return !this.list || !this.list.isVisible();
17973 initQuery : function(){
17975 var v = this.getRawValue();
17977 if(this.tickable && this.editable){
17978 v = this.tickableInputEl().getValue();
17985 doForce : function(){
17986 if(this.inputEl().dom.value.length > 0){
17987 this.inputEl().dom.value =
17988 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17994 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17995 * query allowing the query action to be canceled if needed.
17996 * @param {String} query The SQL query to execute
17997 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17998 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17999 * saved in the current store (defaults to false)
18001 doQuery : function(q, forceAll){
18003 if(q === undefined || q === null){
18008 forceAll: forceAll,
18012 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18017 forceAll = qe.forceAll;
18018 if(forceAll === true || (q.length >= this.minChars)){
18020 this.hasQuery = true;
18022 if(this.lastQuery != q || this.alwaysQuery){
18023 this.lastQuery = q;
18024 if(this.mode == 'local'){
18025 this.selectedIndex = -1;
18027 this.store.clearFilter();
18030 if(this.specialFilter){
18031 this.fireEvent('specialfilter', this);
18036 this.store.filter(this.displayField, q);
18039 this.store.fireEvent("datachanged", this.store);
18046 this.store.baseParams[this.queryParam] = q;
18048 var options = {params : this.getParams(q)};
18051 options.add = true;
18052 options.params.start = this.page * this.pageSize;
18055 this.store.load(options);
18058 * this code will make the page width larger, at the beginning, the list not align correctly,
18059 * we should expand the list on onLoad
18060 * so command out it
18065 this.selectedIndex = -1;
18070 this.loadNext = false;
18074 getParams : function(q){
18076 //p[this.queryParam] = q;
18080 p.limit = this.pageSize;
18086 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18088 collapse : function(){
18089 if(!this.isExpanded()){
18095 this.hasFocus = false;
18099 this.cancelBtn.hide();
18100 this.trigger.show();
18103 this.tickableInputEl().dom.value = '';
18104 this.tickableInputEl().blur();
18109 Roo.get(document).un('mousedown', this.collapseIf, this);
18110 Roo.get(document).un('mousewheel', this.collapseIf, this);
18111 if (!this.editable) {
18112 Roo.get(document).un('keydown', this.listKeyPress, this);
18114 this.fireEvent('collapse', this);
18120 collapseIf : function(e){
18121 var in_combo = e.within(this.el);
18122 var in_list = e.within(this.list);
18123 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18125 if (in_combo || in_list || is_list) {
18126 //e.stopPropagation();
18131 this.onTickableFooterButtonClick(e, false, false);
18139 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18141 expand : function(){
18143 if(this.isExpanded() || !this.hasFocus){
18147 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18148 this.list.setWidth(lw);
18154 this.restrictHeight();
18158 this.tickItems = Roo.apply([], this.item);
18161 this.cancelBtn.show();
18162 this.trigger.hide();
18165 this.tickableInputEl().focus();
18170 Roo.get(document).on('mousedown', this.collapseIf, this);
18171 Roo.get(document).on('mousewheel', this.collapseIf, this);
18172 if (!this.editable) {
18173 Roo.get(document).on('keydown', this.listKeyPress, this);
18176 this.fireEvent('expand', this);
18180 // Implements the default empty TriggerField.onTriggerClick function
18181 onTriggerClick : function(e)
18183 Roo.log('trigger click');
18185 if(this.disabled || !this.triggerList){
18190 this.loadNext = false;
18192 if(this.isExpanded()){
18194 if (!this.blockFocus) {
18195 this.inputEl().focus();
18199 this.hasFocus = true;
18200 if(this.triggerAction == 'all') {
18201 this.doQuery(this.allQuery, true);
18203 this.doQuery(this.getRawValue());
18205 if (!this.blockFocus) {
18206 this.inputEl().focus();
18211 onTickableTriggerClick : function(e)
18218 this.loadNext = false;
18219 this.hasFocus = true;
18221 if(this.triggerAction == 'all') {
18222 this.doQuery(this.allQuery, true);
18224 this.doQuery(this.getRawValue());
18228 onSearchFieldClick : function(e)
18230 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18231 this.onTickableFooterButtonClick(e, false, false);
18235 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18240 this.loadNext = false;
18241 this.hasFocus = true;
18243 if(this.triggerAction == 'all') {
18244 this.doQuery(this.allQuery, true);
18246 this.doQuery(this.getRawValue());
18250 listKeyPress : function(e)
18252 //Roo.log('listkeypress');
18253 // scroll to first matching element based on key pres..
18254 if (e.isSpecialKey()) {
18257 var k = String.fromCharCode(e.getKey()).toUpperCase();
18260 var csel = this.view.getSelectedNodes();
18261 var cselitem = false;
18263 var ix = this.view.indexOf(csel[0]);
18264 cselitem = this.store.getAt(ix);
18265 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18271 this.store.each(function(v) {
18273 // start at existing selection.
18274 if (cselitem.id == v.id) {
18280 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18281 match = this.store.indexOf(v);
18287 if (match === false) {
18288 return true; // no more action?
18291 this.view.select(match);
18292 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18293 sn.scrollIntoView(sn.dom.parentNode, false);
18296 onViewScroll : function(e, t){
18298 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){
18302 this.hasQuery = true;
18304 this.loading = this.list.select('.loading', true).first();
18306 if(this.loading === null){
18307 this.list.createChild({
18309 cls: 'loading roo-select2-more-results roo-select2-active',
18310 html: 'Loading more results...'
18313 this.loading = this.list.select('.loading', true).first();
18315 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18317 this.loading.hide();
18320 this.loading.show();
18325 this.loadNext = true;
18327 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18332 addItem : function(o)
18334 var dv = ''; // display value
18336 if (this.displayField) {
18337 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18339 // this is an error condition!!!
18340 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18347 var choice = this.choices.createChild({
18349 cls: 'roo-select2-search-choice',
18358 cls: 'roo-select2-search-choice-close fa fa-times',
18363 }, this.searchField);
18365 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18367 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18375 this.inputEl().dom.value = '';
18380 onRemoveItem : function(e, _self, o)
18382 e.preventDefault();
18384 this.lastItem = Roo.apply([], this.item);
18386 var index = this.item.indexOf(o.data) * 1;
18389 Roo.log('not this item?!');
18393 this.item.splice(index, 1);
18398 this.fireEvent('remove', this, e);
18404 syncValue : function()
18406 if(!this.item.length){
18413 Roo.each(this.item, function(i){
18414 if(_this.valueField){
18415 value.push(i[_this.valueField]);
18422 this.value = value.join(',');
18424 if(this.hiddenField){
18425 this.hiddenField.dom.value = this.value;
18428 this.store.fireEvent("datachanged", this.store);
18433 clearItem : function()
18435 if(!this.multiple){
18441 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18449 if(this.tickable && !Roo.isTouch){
18450 this.view.refresh();
18454 inputEl: function ()
18456 if(Roo.isIOS && this.useNativeIOS){
18457 return this.el.select('select.roo-ios-select', true).first();
18460 if(Roo.isTouch && this.mobileTouchView){
18461 return this.el.select('input.form-control',true).first();
18465 return this.searchField;
18468 return this.el.select('input.form-control',true).first();
18471 onTickableFooterButtonClick : function(e, btn, el)
18473 e.preventDefault();
18475 this.lastItem = Roo.apply([], this.item);
18477 if(btn && btn.name == 'cancel'){
18478 this.tickItems = Roo.apply([], this.item);
18487 Roo.each(this.tickItems, function(o){
18495 validate : function()
18497 if(this.getVisibilityEl().hasClass('hidden')){
18501 var v = this.getRawValue();
18504 v = this.getValue();
18507 if(this.disabled || this.allowBlank || v.length){
18512 this.markInvalid();
18516 tickableInputEl : function()
18518 if(!this.tickable || !this.editable){
18519 return this.inputEl();
18522 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18526 getAutoCreateTouchView : function()
18531 cls: 'form-group' //input-group
18537 type : this.inputType,
18538 cls : 'form-control x-combo-noedit',
18539 autocomplete: 'new-password',
18540 placeholder : this.placeholder || '',
18545 input.name = this.name;
18549 input.cls += ' input-' + this.size;
18552 if (this.disabled) {
18553 input.disabled = true;
18557 cls : 'roo-combobox-wrap',
18564 inputblock.cls += ' input-group';
18566 inputblock.cn.unshift({
18568 cls : 'input-group-addon input-group-prepend input-group-text',
18573 if(this.removable && !this.multiple){
18574 inputblock.cls += ' roo-removable';
18576 inputblock.cn.push({
18579 cls : 'roo-combo-removable-btn close'
18583 if(this.hasFeedback && !this.allowBlank){
18585 inputblock.cls += ' has-feedback';
18587 inputblock.cn.push({
18589 cls: 'glyphicon form-control-feedback'
18596 inputblock.cls += (this.before) ? '' : ' input-group';
18598 inputblock.cn.push({
18600 cls : 'input-group-addon input-group-append input-group-text',
18606 var ibwrap = inputblock;
18611 cls: 'roo-select2-choices',
18615 cls: 'roo-select2-search-field',
18628 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18633 cls: 'form-hidden-field'
18639 if(!this.multiple && this.showToggleBtn){
18645 if (this.caret != false) {
18648 cls: 'fa fa-' + this.caret
18655 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18657 Roo.bootstrap.version == 3 ? caret : '',
18660 cls: 'combobox-clear',
18674 combobox.cls += ' roo-select2-container-multi';
18677 var required = this.allowBlank ? {
18679 style: 'display: none'
18682 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18683 tooltip : 'This field is required'
18686 var align = this.labelAlign || this.parentLabelAlign();
18688 if (align ==='left' && this.fieldLabel.length) {
18694 cls : 'control-label col-form-label',
18695 html : this.fieldLabel
18699 cls : 'roo-combobox-wrap ',
18706 var labelCfg = cfg.cn[1];
18707 var contentCfg = cfg.cn[2];
18710 if(this.indicatorpos == 'right'){
18715 cls : 'control-label col-form-label',
18719 html : this.fieldLabel
18725 cls : "roo-combobox-wrap ",
18733 labelCfg = cfg.cn[0];
18734 contentCfg = cfg.cn[1];
18739 if(this.labelWidth > 12){
18740 labelCfg.style = "width: " + this.labelWidth + 'px';
18743 if(this.labelWidth < 13 && this.labelmd == 0){
18744 this.labelmd = this.labelWidth;
18747 if(this.labellg > 0){
18748 labelCfg.cls += ' col-lg-' + this.labellg;
18749 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18752 if(this.labelmd > 0){
18753 labelCfg.cls += ' col-md-' + this.labelmd;
18754 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18757 if(this.labelsm > 0){
18758 labelCfg.cls += ' col-sm-' + this.labelsm;
18759 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18762 if(this.labelxs > 0){
18763 labelCfg.cls += ' col-xs-' + this.labelxs;
18764 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18768 } else if ( this.fieldLabel.length) {
18773 cls : 'control-label',
18774 html : this.fieldLabel
18785 if(this.indicatorpos == 'right'){
18789 cls : 'control-label',
18790 html : this.fieldLabel,
18808 var settings = this;
18810 ['xs','sm','md','lg'].map(function(size){
18811 if (settings[size]) {
18812 cfg.cls += ' col-' + size + '-' + settings[size];
18819 initTouchView : function()
18821 this.renderTouchView();
18823 this.touchViewEl.on('scroll', function(){
18824 this.el.dom.scrollTop = 0;
18827 this.originalValue = this.getValue();
18829 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18831 this.inputEl().on("click", this.showTouchView, this);
18832 if (this.triggerEl) {
18833 this.triggerEl.on("click", this.showTouchView, this);
18837 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18838 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18840 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18842 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18843 this.store.on('load', this.onTouchViewLoad, this);
18844 this.store.on('loadexception', this.onTouchViewLoadException, this);
18846 if(this.hiddenName){
18848 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18850 this.hiddenField.dom.value =
18851 this.hiddenValue !== undefined ? this.hiddenValue :
18852 this.value !== undefined ? this.value : '';
18854 this.el.dom.removeAttribute('name');
18855 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18859 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18860 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18863 if(this.removable && !this.multiple){
18864 var close = this.closeTriggerEl();
18866 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18867 close.on('click', this.removeBtnClick, this, close);
18871 * fix the bug in Safari iOS8
18873 this.inputEl().on("focus", function(e){
18874 document.activeElement.blur();
18877 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18884 renderTouchView : function()
18886 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18887 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18889 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18890 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18892 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18893 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18894 this.touchViewBodyEl.setStyle('overflow', 'auto');
18896 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18897 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18899 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18900 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18904 showTouchView : function()
18910 this.touchViewHeaderEl.hide();
18912 if(this.modalTitle.length){
18913 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18914 this.touchViewHeaderEl.show();
18917 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18918 this.touchViewEl.show();
18920 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18922 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18923 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18925 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18927 if(this.modalTitle.length){
18928 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18931 this.touchViewBodyEl.setHeight(bodyHeight);
18935 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18937 this.touchViewEl.addClass(['in','show']);
18940 if(this._touchViewMask){
18941 Roo.get(document.body).addClass("x-body-masked");
18942 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18943 this._touchViewMask.setStyle('z-index', 10000);
18944 this._touchViewMask.addClass('show');
18947 this.doTouchViewQuery();
18951 hideTouchView : function()
18953 this.touchViewEl.removeClass(['in','show']);
18957 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18959 this.touchViewEl.setStyle('display', 'none');
18962 if(this._touchViewMask){
18963 this._touchViewMask.removeClass('show');
18964 Roo.get(document.body).removeClass("x-body-masked");
18968 setTouchViewValue : function()
18975 Roo.each(this.tickItems, function(o){
18980 this.hideTouchView();
18983 doTouchViewQuery : function()
18992 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18996 if(!this.alwaysQuery || this.mode == 'local'){
18997 this.onTouchViewLoad();
19004 onTouchViewBeforeLoad : function(combo,opts)
19010 onTouchViewLoad : function()
19012 if(this.store.getCount() < 1){
19013 this.onTouchViewEmptyResults();
19017 this.clearTouchView();
19019 var rawValue = this.getRawValue();
19021 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19023 this.tickItems = [];
19025 this.store.data.each(function(d, rowIndex){
19026 var row = this.touchViewListGroup.createChild(template);
19028 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19029 row.addClass(d.data.cls);
19032 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19035 html : d.data[this.displayField]
19038 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19039 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19042 row.removeClass('selected');
19043 if(!this.multiple && this.valueField &&
19044 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19047 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19048 row.addClass('selected');
19051 if(this.multiple && this.valueField &&
19052 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19056 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19057 this.tickItems.push(d.data);
19060 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19064 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19066 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19068 if(this.modalTitle.length){
19069 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19072 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19074 if(this.mobile_restrict_height && listHeight < bodyHeight){
19075 this.touchViewBodyEl.setHeight(listHeight);
19080 if(firstChecked && listHeight > bodyHeight){
19081 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19086 onTouchViewLoadException : function()
19088 this.hideTouchView();
19091 onTouchViewEmptyResults : function()
19093 this.clearTouchView();
19095 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19097 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19101 clearTouchView : function()
19103 this.touchViewListGroup.dom.innerHTML = '';
19106 onTouchViewClick : function(e, el, o)
19108 e.preventDefault();
19111 var rowIndex = o.rowIndex;
19113 var r = this.store.getAt(rowIndex);
19115 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19117 if(!this.multiple){
19118 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19119 c.dom.removeAttribute('checked');
19122 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19124 this.setFromData(r.data);
19126 var close = this.closeTriggerEl();
19132 this.hideTouchView();
19134 this.fireEvent('select', this, r, rowIndex);
19139 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19140 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19141 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19145 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19146 this.addItem(r.data);
19147 this.tickItems.push(r.data);
19151 getAutoCreateNativeIOS : function()
19154 cls: 'form-group' //input-group,
19159 cls : 'roo-ios-select'
19163 combobox.name = this.name;
19166 if (this.disabled) {
19167 combobox.disabled = true;
19170 var settings = this;
19172 ['xs','sm','md','lg'].map(function(size){
19173 if (settings[size]) {
19174 cfg.cls += ' col-' + size + '-' + settings[size];
19184 initIOSView : function()
19186 this.store.on('load', this.onIOSViewLoad, this);
19191 onIOSViewLoad : function()
19193 if(this.store.getCount() < 1){
19197 this.clearIOSView();
19199 if(this.allowBlank) {
19201 var default_text = '-- SELECT --';
19203 if(this.placeholder.length){
19204 default_text = this.placeholder;
19207 if(this.emptyTitle.length){
19208 default_text += ' - ' + this.emptyTitle + ' -';
19211 var opt = this.inputEl().createChild({
19214 html : default_text
19218 o[this.valueField] = 0;
19219 o[this.displayField] = default_text;
19221 this.ios_options.push({
19228 this.store.data.each(function(d, rowIndex){
19232 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19233 html = d.data[this.displayField];
19238 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19239 value = d.data[this.valueField];
19248 if(this.value == d.data[this.valueField]){
19249 option['selected'] = true;
19252 var opt = this.inputEl().createChild(option);
19254 this.ios_options.push({
19261 this.inputEl().on('change', function(){
19262 this.fireEvent('select', this);
19267 clearIOSView: function()
19269 this.inputEl().dom.innerHTML = '';
19271 this.ios_options = [];
19274 setIOSValue: function(v)
19278 if(!this.ios_options){
19282 Roo.each(this.ios_options, function(opts){
19284 opts.el.dom.removeAttribute('selected');
19286 if(opts.data[this.valueField] != v){
19290 opts.el.dom.setAttribute('selected', true);
19296 * @cfg {Boolean} grow
19300 * @cfg {Number} growMin
19304 * @cfg {Number} growMax
19313 Roo.apply(Roo.bootstrap.ComboBox, {
19317 cls: 'modal-header',
19339 cls: 'list-group-item',
19343 cls: 'roo-combobox-list-group-item-value'
19347 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19361 listItemCheckbox : {
19363 cls: 'list-group-item',
19367 cls: 'roo-combobox-list-group-item-value'
19371 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19387 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19392 cls: 'modal-footer',
19400 cls: 'col-xs-6 text-left',
19403 cls: 'btn btn-danger roo-touch-view-cancel',
19409 cls: 'col-xs-6 text-right',
19412 cls: 'btn btn-success roo-touch-view-ok',
19423 Roo.apply(Roo.bootstrap.ComboBox, {
19425 touchViewTemplate : {
19427 cls: 'modal fade roo-combobox-touch-view',
19431 cls: 'modal-dialog',
19432 style : 'position:fixed', // we have to fix position....
19436 cls: 'modal-content',
19438 Roo.bootstrap.ComboBox.header,
19439 Roo.bootstrap.ComboBox.body,
19440 Roo.bootstrap.ComboBox.footer
19449 * Ext JS Library 1.1.1
19450 * Copyright(c) 2006-2007, Ext JS, LLC.
19452 * Originally Released Under LGPL - original licence link has changed is not relivant.
19455 * <script type="text/javascript">
19460 * @extends Roo.util.Observable
19461 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19462 * This class also supports single and multi selection modes. <br>
19463 * Create a data model bound view:
19465 var store = new Roo.data.Store(...);
19467 var view = new Roo.View({
19469 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19471 singleSelect: true,
19472 selectedClass: "ydataview-selected",
19476 // listen for node click?
19477 view.on("click", function(vw, index, node, e){
19478 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19482 dataModel.load("foobar.xml");
19484 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19486 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19487 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19489 * Note: old style constructor is still suported (container, template, config)
19492 * Create a new View
19493 * @param {Object} config The config object
19496 Roo.View = function(config, depreciated_tpl, depreciated_config){
19498 this.parent = false;
19500 if (typeof(depreciated_tpl) == 'undefined') {
19501 // new way.. - universal constructor.
19502 Roo.apply(this, config);
19503 this.el = Roo.get(this.el);
19506 this.el = Roo.get(config);
19507 this.tpl = depreciated_tpl;
19508 Roo.apply(this, depreciated_config);
19510 this.wrapEl = this.el.wrap().wrap();
19511 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19514 if(typeof(this.tpl) == "string"){
19515 this.tpl = new Roo.Template(this.tpl);
19517 // support xtype ctors..
19518 this.tpl = new Roo.factory(this.tpl, Roo);
19522 this.tpl.compile();
19527 * @event beforeclick
19528 * Fires before a click is processed. Returns false to cancel the default action.
19529 * @param {Roo.View} this
19530 * @param {Number} index The index of the target node
19531 * @param {HTMLElement} node The target node
19532 * @param {Roo.EventObject} e The raw event object
19534 "beforeclick" : true,
19537 * Fires when a template node is clicked.
19538 * @param {Roo.View} this
19539 * @param {Number} index The index of the target node
19540 * @param {HTMLElement} node The target node
19541 * @param {Roo.EventObject} e The raw event object
19546 * Fires when a template node is double clicked.
19547 * @param {Roo.View} this
19548 * @param {Number} index The index of the target node
19549 * @param {HTMLElement} node The target node
19550 * @param {Roo.EventObject} e The raw event object
19554 * @event contextmenu
19555 * Fires when a template node is right clicked.
19556 * @param {Roo.View} this
19557 * @param {Number} index The index of the target node
19558 * @param {HTMLElement} node The target node
19559 * @param {Roo.EventObject} e The raw event object
19561 "contextmenu" : true,
19563 * @event selectionchange
19564 * Fires when the selected nodes change.
19565 * @param {Roo.View} this
19566 * @param {Array} selections Array of the selected nodes
19568 "selectionchange" : true,
19571 * @event beforeselect
19572 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19573 * @param {Roo.View} this
19574 * @param {HTMLElement} node The node to be selected
19575 * @param {Array} selections Array of currently selected nodes
19577 "beforeselect" : true,
19579 * @event preparedata
19580 * Fires on every row to render, to allow you to change the data.
19581 * @param {Roo.View} this
19582 * @param {Object} data to be rendered (change this)
19584 "preparedata" : true
19592 "click": this.onClick,
19593 "dblclick": this.onDblClick,
19594 "contextmenu": this.onContextMenu,
19598 this.selections = [];
19600 this.cmp = new Roo.CompositeElementLite([]);
19602 this.store = Roo.factory(this.store, Roo.data);
19603 this.setStore(this.store, true);
19606 if ( this.footer && this.footer.xtype) {
19608 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19610 this.footer.dataSource = this.store;
19611 this.footer.container = fctr;
19612 this.footer = Roo.factory(this.footer, Roo);
19613 fctr.insertFirst(this.el);
19615 // this is a bit insane - as the paging toolbar seems to detach the el..
19616 // dom.parentNode.parentNode.parentNode
19617 // they get detached?
19621 Roo.View.superclass.constructor.call(this);
19626 Roo.extend(Roo.View, Roo.util.Observable, {
19629 * @cfg {Roo.data.Store} store Data store to load data from.
19634 * @cfg {String|Roo.Element} el The container element.
19639 * @cfg {String|Roo.Template} tpl The template used by this View
19643 * @cfg {String} dataName the named area of the template to use as the data area
19644 * Works with domtemplates roo-name="name"
19648 * @cfg {String} selectedClass The css class to add to selected nodes
19650 selectedClass : "x-view-selected",
19652 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19657 * @cfg {String} text to display on mask (default Loading)
19661 * @cfg {Boolean} multiSelect Allow multiple selection
19663 multiSelect : false,
19665 * @cfg {Boolean} singleSelect Allow single selection
19667 singleSelect: false,
19670 * @cfg {Boolean} toggleSelect - selecting
19672 toggleSelect : false,
19675 * @cfg {Boolean} tickable - selecting
19680 * Returns the element this view is bound to.
19681 * @return {Roo.Element}
19683 getEl : function(){
19684 return this.wrapEl;
19690 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19692 refresh : function(){
19693 //Roo.log('refresh');
19696 // if we are using something like 'domtemplate', then
19697 // the what gets used is:
19698 // t.applySubtemplate(NAME, data, wrapping data..)
19699 // the outer template then get' applied with
19700 // the store 'extra data'
19701 // and the body get's added to the
19702 // roo-name="data" node?
19703 // <span class='roo-tpl-{name}'></span> ?????
19707 this.clearSelections();
19708 this.el.update("");
19710 var records = this.store.getRange();
19711 if(records.length < 1) {
19713 // is this valid?? = should it render a template??
19715 this.el.update(this.emptyText);
19719 if (this.dataName) {
19720 this.el.update(t.apply(this.store.meta)); //????
19721 el = this.el.child('.roo-tpl-' + this.dataName);
19724 for(var i = 0, len = records.length; i < len; i++){
19725 var data = this.prepareData(records[i].data, i, records[i]);
19726 this.fireEvent("preparedata", this, data, i, records[i]);
19728 var d = Roo.apply({}, data);
19731 Roo.apply(d, {'roo-id' : Roo.id()});
19735 Roo.each(this.parent.item, function(item){
19736 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19739 Roo.apply(d, {'roo-data-checked' : 'checked'});
19743 html[html.length] = Roo.util.Format.trim(
19745 t.applySubtemplate(this.dataName, d, this.store.meta) :
19752 el.update(html.join(""));
19753 this.nodes = el.dom.childNodes;
19754 this.updateIndexes(0);
19759 * Function to override to reformat the data that is sent to
19760 * the template for each node.
19761 * DEPRICATED - use the preparedata event handler.
19762 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19763 * a JSON object for an UpdateManager bound view).
19765 prepareData : function(data, index, record)
19767 this.fireEvent("preparedata", this, data, index, record);
19771 onUpdate : function(ds, record){
19772 // Roo.log('on update');
19773 this.clearSelections();
19774 var index = this.store.indexOf(record);
19775 var n = this.nodes[index];
19776 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19777 n.parentNode.removeChild(n);
19778 this.updateIndexes(index, index);
19784 onAdd : function(ds, records, index)
19786 //Roo.log(['on Add', ds, records, index] );
19787 this.clearSelections();
19788 if(this.nodes.length == 0){
19792 var n = this.nodes[index];
19793 for(var i = 0, len = records.length; i < len; i++){
19794 var d = this.prepareData(records[i].data, i, records[i]);
19796 this.tpl.insertBefore(n, d);
19799 this.tpl.append(this.el, d);
19802 this.updateIndexes(index);
19805 onRemove : function(ds, record, index){
19806 // Roo.log('onRemove');
19807 this.clearSelections();
19808 var el = this.dataName ?
19809 this.el.child('.roo-tpl-' + this.dataName) :
19812 el.dom.removeChild(this.nodes[index]);
19813 this.updateIndexes(index);
19817 * Refresh an individual node.
19818 * @param {Number} index
19820 refreshNode : function(index){
19821 this.onUpdate(this.store, this.store.getAt(index));
19824 updateIndexes : function(startIndex, endIndex){
19825 var ns = this.nodes;
19826 startIndex = startIndex || 0;
19827 endIndex = endIndex || ns.length - 1;
19828 for(var i = startIndex; i <= endIndex; i++){
19829 ns[i].nodeIndex = i;
19834 * Changes the data store this view uses and refresh the view.
19835 * @param {Store} store
19837 setStore : function(store, initial){
19838 if(!initial && this.store){
19839 this.store.un("datachanged", this.refresh);
19840 this.store.un("add", this.onAdd);
19841 this.store.un("remove", this.onRemove);
19842 this.store.un("update", this.onUpdate);
19843 this.store.un("clear", this.refresh);
19844 this.store.un("beforeload", this.onBeforeLoad);
19845 this.store.un("load", this.onLoad);
19846 this.store.un("loadexception", this.onLoad);
19850 store.on("datachanged", this.refresh, this);
19851 store.on("add", this.onAdd, this);
19852 store.on("remove", this.onRemove, this);
19853 store.on("update", this.onUpdate, this);
19854 store.on("clear", this.refresh, this);
19855 store.on("beforeload", this.onBeforeLoad, this);
19856 store.on("load", this.onLoad, this);
19857 store.on("loadexception", this.onLoad, this);
19865 * onbeforeLoad - masks the loading area.
19868 onBeforeLoad : function(store,opts)
19870 //Roo.log('onBeforeLoad');
19872 this.el.update("");
19874 this.el.mask(this.mask ? this.mask : "Loading" );
19876 onLoad : function ()
19883 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19884 * @param {HTMLElement} node
19885 * @return {HTMLElement} The template node
19887 findItemFromChild : function(node){
19888 var el = this.dataName ?
19889 this.el.child('.roo-tpl-' + this.dataName,true) :
19892 if(!node || node.parentNode == el){
19895 var p = node.parentNode;
19896 while(p && p != el){
19897 if(p.parentNode == el){
19906 onClick : function(e){
19907 var item = this.findItemFromChild(e.getTarget());
19909 var index = this.indexOf(item);
19910 if(this.onItemClick(item, index, e) !== false){
19911 this.fireEvent("click", this, index, item, e);
19914 this.clearSelections();
19919 onContextMenu : function(e){
19920 var item = this.findItemFromChild(e.getTarget());
19922 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19927 onDblClick : function(e){
19928 var item = this.findItemFromChild(e.getTarget());
19930 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19934 onItemClick : function(item, index, e)
19936 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19939 if (this.toggleSelect) {
19940 var m = this.isSelected(item) ? 'unselect' : 'select';
19943 _t[m](item, true, false);
19946 if(this.multiSelect || this.singleSelect){
19947 if(this.multiSelect && e.shiftKey && this.lastSelection){
19948 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19950 this.select(item, this.multiSelect && e.ctrlKey);
19951 this.lastSelection = item;
19954 if(!this.tickable){
19955 e.preventDefault();
19963 * Get the number of selected nodes.
19966 getSelectionCount : function(){
19967 return this.selections.length;
19971 * Get the currently selected nodes.
19972 * @return {Array} An array of HTMLElements
19974 getSelectedNodes : function(){
19975 return this.selections;
19979 * Get the indexes of the selected nodes.
19982 getSelectedIndexes : function(){
19983 var indexes = [], s = this.selections;
19984 for(var i = 0, len = s.length; i < len; i++){
19985 indexes.push(s[i].nodeIndex);
19991 * Clear all selections
19992 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19994 clearSelections : function(suppressEvent){
19995 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19996 this.cmp.elements = this.selections;
19997 this.cmp.removeClass(this.selectedClass);
19998 this.selections = [];
19999 if(!suppressEvent){
20000 this.fireEvent("selectionchange", this, this.selections);
20006 * Returns true if the passed node is selected
20007 * @param {HTMLElement/Number} node The node or node index
20008 * @return {Boolean}
20010 isSelected : function(node){
20011 var s = this.selections;
20015 node = this.getNode(node);
20016 return s.indexOf(node) !== -1;
20021 * @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
20022 * @param {Boolean} keepExisting (optional) true to keep existing selections
20023 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20025 select : function(nodeInfo, keepExisting, suppressEvent){
20026 if(nodeInfo instanceof Array){
20028 this.clearSelections(true);
20030 for(var i = 0, len = nodeInfo.length; i < len; i++){
20031 this.select(nodeInfo[i], true, true);
20035 var node = this.getNode(nodeInfo);
20036 if(!node || this.isSelected(node)){
20037 return; // already selected.
20040 this.clearSelections(true);
20043 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20044 Roo.fly(node).addClass(this.selectedClass);
20045 this.selections.push(node);
20046 if(!suppressEvent){
20047 this.fireEvent("selectionchange", this, this.selections);
20055 * @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
20056 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20057 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20059 unselect : function(nodeInfo, keepExisting, suppressEvent)
20061 if(nodeInfo instanceof Array){
20062 Roo.each(this.selections, function(s) {
20063 this.unselect(s, nodeInfo);
20067 var node = this.getNode(nodeInfo);
20068 if(!node || !this.isSelected(node)){
20069 //Roo.log("not selected");
20070 return; // not selected.
20074 Roo.each(this.selections, function(s) {
20076 Roo.fly(node).removeClass(this.selectedClass);
20083 this.selections= ns;
20084 this.fireEvent("selectionchange", this, this.selections);
20088 * Gets a template node.
20089 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20090 * @return {HTMLElement} The node or null if it wasn't found
20092 getNode : function(nodeInfo){
20093 if(typeof nodeInfo == "string"){
20094 return document.getElementById(nodeInfo);
20095 }else if(typeof nodeInfo == "number"){
20096 return this.nodes[nodeInfo];
20102 * Gets a range template nodes.
20103 * @param {Number} startIndex
20104 * @param {Number} endIndex
20105 * @return {Array} An array of nodes
20107 getNodes : function(start, end){
20108 var ns = this.nodes;
20109 start = start || 0;
20110 end = typeof end == "undefined" ? ns.length - 1 : end;
20113 for(var i = start; i <= end; i++){
20117 for(var i = start; i >= end; i--){
20125 * Finds the index of the passed node
20126 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20127 * @return {Number} The index of the node or -1
20129 indexOf : function(node){
20130 node = this.getNode(node);
20131 if(typeof node.nodeIndex == "number"){
20132 return node.nodeIndex;
20134 var ns = this.nodes;
20135 for(var i = 0, len = ns.length; i < len; i++){
20146 * based on jquery fullcalendar
20150 Roo.bootstrap = Roo.bootstrap || {};
20152 * @class Roo.bootstrap.Calendar
20153 * @extends Roo.bootstrap.Component
20154 * Bootstrap Calendar class
20155 * @cfg {Boolean} loadMask (true|false) default false
20156 * @cfg {Object} header generate the user specific header of the calendar, default false
20159 * Create a new Container
20160 * @param {Object} config The config object
20165 Roo.bootstrap.Calendar = function(config){
20166 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20170 * Fires when a date is selected
20171 * @param {DatePicker} this
20172 * @param {Date} date The selected date
20176 * @event monthchange
20177 * Fires when the displayed month changes
20178 * @param {DatePicker} this
20179 * @param {Date} date The selected month
20181 'monthchange': true,
20183 * @event evententer
20184 * Fires when mouse over an event
20185 * @param {Calendar} this
20186 * @param {event} Event
20188 'evententer': true,
20190 * @event eventleave
20191 * Fires when the mouse leaves an
20192 * @param {Calendar} this
20195 'eventleave': true,
20197 * @event eventclick
20198 * Fires when the mouse click an
20199 * @param {Calendar} this
20208 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20211 * @cfg {Roo.data.Store} store
20212 * The data source for the calendar
20216 * @cfg {Number} startDay
20217 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20225 getAutoCreate : function(){
20228 var fc_button = function(name, corner, style, content ) {
20229 return Roo.apply({},{
20231 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20233 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20236 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20247 style : 'width:100%',
20254 cls : 'fc-header-left',
20256 fc_button('prev', 'left', 'arrow', '‹' ),
20257 fc_button('next', 'right', 'arrow', '›' ),
20258 { tag: 'span', cls: 'fc-header-space' },
20259 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20267 cls : 'fc-header-center',
20271 cls: 'fc-header-title',
20274 html : 'month / year'
20282 cls : 'fc-header-right',
20284 /* fc_button('month', 'left', '', 'month' ),
20285 fc_button('week', '', '', 'week' ),
20286 fc_button('day', 'right', '', 'day' )
20298 header = this.header;
20301 var cal_heads = function() {
20303 // fixme - handle this.
20305 for (var i =0; i < Date.dayNames.length; i++) {
20306 var d = Date.dayNames[i];
20309 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20310 html : d.substring(0,3)
20314 ret[0].cls += ' fc-first';
20315 ret[6].cls += ' fc-last';
20318 var cal_cell = function(n) {
20321 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20326 cls: 'fc-day-number',
20330 cls: 'fc-day-content',
20334 style: 'position: relative;' // height: 17px;
20346 var cal_rows = function() {
20349 for (var r = 0; r < 6; r++) {
20356 for (var i =0; i < Date.dayNames.length; i++) {
20357 var d = Date.dayNames[i];
20358 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20361 row.cn[0].cls+=' fc-first';
20362 row.cn[0].cn[0].style = 'min-height:90px';
20363 row.cn[6].cls+=' fc-last';
20367 ret[0].cls += ' fc-first';
20368 ret[4].cls += ' fc-prev-last';
20369 ret[5].cls += ' fc-last';
20376 cls: 'fc-border-separate',
20377 style : 'width:100%',
20385 cls : 'fc-first fc-last',
20403 cls : 'fc-content',
20404 style : "position: relative;",
20407 cls : 'fc-view fc-view-month fc-grid',
20408 style : 'position: relative',
20409 unselectable : 'on',
20412 cls : 'fc-event-container',
20413 style : 'position:absolute;z-index:8;top:0;left:0;'
20431 initEvents : function()
20434 throw "can not find store for calendar";
20440 style: "text-align:center",
20444 style: "background-color:white;width:50%;margin:250 auto",
20448 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20459 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20461 var size = this.el.select('.fc-content', true).first().getSize();
20462 this.maskEl.setSize(size.width, size.height);
20463 this.maskEl.enableDisplayMode("block");
20464 if(!this.loadMask){
20465 this.maskEl.hide();
20468 this.store = Roo.factory(this.store, Roo.data);
20469 this.store.on('load', this.onLoad, this);
20470 this.store.on('beforeload', this.onBeforeLoad, this);
20474 this.cells = this.el.select('.fc-day',true);
20475 //Roo.log(this.cells);
20476 this.textNodes = this.el.query('.fc-day-number');
20477 this.cells.addClassOnOver('fc-state-hover');
20479 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20480 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20481 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20482 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20484 this.on('monthchange', this.onMonthChange, this);
20486 this.update(new Date().clearTime());
20489 resize : function() {
20490 var sz = this.el.getSize();
20492 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20493 this.el.select('.fc-day-content div',true).setHeight(34);
20498 showPrevMonth : function(e){
20499 this.update(this.activeDate.add("mo", -1));
20501 showToday : function(e){
20502 this.update(new Date().clearTime());
20505 showNextMonth : function(e){
20506 this.update(this.activeDate.add("mo", 1));
20510 showPrevYear : function(){
20511 this.update(this.activeDate.add("y", -1));
20515 showNextYear : function(){
20516 this.update(this.activeDate.add("y", 1));
20521 update : function(date)
20523 var vd = this.activeDate;
20524 this.activeDate = date;
20525 // if(vd && this.el){
20526 // var t = date.getTime();
20527 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20528 // Roo.log('using add remove');
20530 // this.fireEvent('monthchange', this, date);
20532 // this.cells.removeClass("fc-state-highlight");
20533 // this.cells.each(function(c){
20534 // if(c.dateValue == t){
20535 // c.addClass("fc-state-highlight");
20536 // setTimeout(function(){
20537 // try{c.dom.firstChild.focus();}catch(e){}
20547 var days = date.getDaysInMonth();
20549 var firstOfMonth = date.getFirstDateOfMonth();
20550 var startingPos = firstOfMonth.getDay()-this.startDay;
20552 if(startingPos < this.startDay){
20556 var pm = date.add(Date.MONTH, -1);
20557 var prevStart = pm.getDaysInMonth()-startingPos;
20559 this.cells = this.el.select('.fc-day',true);
20560 this.textNodes = this.el.query('.fc-day-number');
20561 this.cells.addClassOnOver('fc-state-hover');
20563 var cells = this.cells.elements;
20564 var textEls = this.textNodes;
20566 Roo.each(cells, function(cell){
20567 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20570 days += startingPos;
20572 // convert everything to numbers so it's fast
20573 var day = 86400000;
20574 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20577 //Roo.log(prevStart);
20579 var today = new Date().clearTime().getTime();
20580 var sel = date.clearTime().getTime();
20581 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20582 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20583 var ddMatch = this.disabledDatesRE;
20584 var ddText = this.disabledDatesText;
20585 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20586 var ddaysText = this.disabledDaysText;
20587 var format = this.format;
20589 var setCellClass = function(cal, cell){
20593 //Roo.log('set Cell Class');
20595 var t = d.getTime();
20599 cell.dateValue = t;
20601 cell.className += " fc-today";
20602 cell.className += " fc-state-highlight";
20603 cell.title = cal.todayText;
20606 // disable highlight in other month..
20607 //cell.className += " fc-state-highlight";
20612 cell.className = " fc-state-disabled";
20613 cell.title = cal.minText;
20617 cell.className = " fc-state-disabled";
20618 cell.title = cal.maxText;
20622 if(ddays.indexOf(d.getDay()) != -1){
20623 cell.title = ddaysText;
20624 cell.className = " fc-state-disabled";
20627 if(ddMatch && format){
20628 var fvalue = d.dateFormat(format);
20629 if(ddMatch.test(fvalue)){
20630 cell.title = ddText.replace("%0", fvalue);
20631 cell.className = " fc-state-disabled";
20635 if (!cell.initialClassName) {
20636 cell.initialClassName = cell.dom.className;
20639 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20644 for(; i < startingPos; i++) {
20645 textEls[i].innerHTML = (++prevStart);
20646 d.setDate(d.getDate()+1);
20648 cells[i].className = "fc-past fc-other-month";
20649 setCellClass(this, cells[i]);
20654 for(; i < days; i++){
20655 intDay = i - startingPos + 1;
20656 textEls[i].innerHTML = (intDay);
20657 d.setDate(d.getDate()+1);
20659 cells[i].className = ''; // "x-date-active";
20660 setCellClass(this, cells[i]);
20664 for(; i < 42; i++) {
20665 textEls[i].innerHTML = (++extraDays);
20666 d.setDate(d.getDate()+1);
20668 cells[i].className = "fc-future fc-other-month";
20669 setCellClass(this, cells[i]);
20672 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20674 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20676 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20677 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20679 if(totalRows != 6){
20680 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20681 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20684 this.fireEvent('monthchange', this, date);
20688 if(!this.internalRender){
20689 var main = this.el.dom.firstChild;
20690 var w = main.offsetWidth;
20691 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20692 Roo.fly(main).setWidth(w);
20693 this.internalRender = true;
20694 // opera does not respect the auto grow header center column
20695 // then, after it gets a width opera refuses to recalculate
20696 // without a second pass
20697 if(Roo.isOpera && !this.secondPass){
20698 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20699 this.secondPass = true;
20700 this.update.defer(10, this, [date]);
20707 findCell : function(dt) {
20708 dt = dt.clearTime().getTime();
20710 this.cells.each(function(c){
20711 //Roo.log("check " +c.dateValue + '?=' + dt);
20712 if(c.dateValue == dt){
20722 findCells : function(ev) {
20723 var s = ev.start.clone().clearTime().getTime();
20725 var e= ev.end.clone().clearTime().getTime();
20728 this.cells.each(function(c){
20729 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20731 if(c.dateValue > e){
20734 if(c.dateValue < s){
20743 // findBestRow: function(cells)
20747 // for (var i =0 ; i < cells.length;i++) {
20748 // ret = Math.max(cells[i].rows || 0,ret);
20755 addItem : function(ev)
20757 // look for vertical location slot in
20758 var cells = this.findCells(ev);
20760 // ev.row = this.findBestRow(cells);
20762 // work out the location.
20766 for(var i =0; i < cells.length; i++) {
20768 cells[i].row = cells[0].row;
20771 cells[i].row = cells[i].row + 1;
20781 if (crow.start.getY() == cells[i].getY()) {
20783 crow.end = cells[i];
20800 cells[0].events.push(ev);
20802 this.calevents.push(ev);
20805 clearEvents: function() {
20807 if(!this.calevents){
20811 Roo.each(this.cells.elements, function(c){
20817 Roo.each(this.calevents, function(e) {
20818 Roo.each(e.els, function(el) {
20819 el.un('mouseenter' ,this.onEventEnter, this);
20820 el.un('mouseleave' ,this.onEventLeave, this);
20825 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20831 renderEvents: function()
20835 this.cells.each(function(c) {
20844 if(c.row != c.events.length){
20845 r = 4 - (4 - (c.row - c.events.length));
20848 c.events = ev.slice(0, r);
20849 c.more = ev.slice(r);
20851 if(c.more.length && c.more.length == 1){
20852 c.events.push(c.more.pop());
20855 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20859 this.cells.each(function(c) {
20861 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20864 for (var e = 0; e < c.events.length; e++){
20865 var ev = c.events[e];
20866 var rows = ev.rows;
20868 for(var i = 0; i < rows.length; i++) {
20870 // how many rows should it span..
20873 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20874 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20876 unselectable : "on",
20879 cls: 'fc-event-inner',
20883 // cls: 'fc-event-time',
20884 // html : cells.length > 1 ? '' : ev.time
20888 cls: 'fc-event-title',
20889 html : String.format('{0}', ev.title)
20896 cls: 'ui-resizable-handle ui-resizable-e',
20897 html : '  '
20904 cfg.cls += ' fc-event-start';
20906 if ((i+1) == rows.length) {
20907 cfg.cls += ' fc-event-end';
20910 var ctr = _this.el.select('.fc-event-container',true).first();
20911 var cg = ctr.createChild(cfg);
20913 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20914 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20916 var r = (c.more.length) ? 1 : 0;
20917 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20918 cg.setWidth(ebox.right - sbox.x -2);
20920 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20921 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20922 cg.on('click', _this.onEventClick, _this, ev);
20933 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20934 style : 'position: absolute',
20935 unselectable : "on",
20938 cls: 'fc-event-inner',
20942 cls: 'fc-event-title',
20950 cls: 'ui-resizable-handle ui-resizable-e',
20951 html : '  '
20957 var ctr = _this.el.select('.fc-event-container',true).first();
20958 var cg = ctr.createChild(cfg);
20960 var sbox = c.select('.fc-day-content',true).first().getBox();
20961 var ebox = c.select('.fc-day-content',true).first().getBox();
20963 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20964 cg.setWidth(ebox.right - sbox.x -2);
20966 cg.on('click', _this.onMoreEventClick, _this, c.more);
20976 onEventEnter: function (e, el,event,d) {
20977 this.fireEvent('evententer', this, el, event);
20980 onEventLeave: function (e, el,event,d) {
20981 this.fireEvent('eventleave', this, el, event);
20984 onEventClick: function (e, el,event,d) {
20985 this.fireEvent('eventclick', this, el, event);
20988 onMonthChange: function () {
20992 onMoreEventClick: function(e, el, more)
20996 this.calpopover.placement = 'right';
20997 this.calpopover.setTitle('More');
20999 this.calpopover.setContent('');
21001 var ctr = this.calpopover.el.select('.popover-content', true).first();
21003 Roo.each(more, function(m){
21005 cls : 'fc-event-hori fc-event-draggable',
21008 var cg = ctr.createChild(cfg);
21010 cg.on('click', _this.onEventClick, _this, m);
21013 this.calpopover.show(el);
21018 onLoad: function ()
21020 this.calevents = [];
21023 if(this.store.getCount() > 0){
21024 this.store.data.each(function(d){
21027 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21028 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21029 time : d.data.start_time,
21030 title : d.data.title,
21031 description : d.data.description,
21032 venue : d.data.venue
21037 this.renderEvents();
21039 if(this.calevents.length && this.loadMask){
21040 this.maskEl.hide();
21044 onBeforeLoad: function()
21046 this.clearEvents();
21048 this.maskEl.show();
21062 * @class Roo.bootstrap.Popover
21063 * @extends Roo.bootstrap.Component
21065 * Bootstrap Popover class
21066 * @cfg {String} html contents of the popover (or false to use children..)
21067 * @cfg {String} title of popover (or false to hide)
21068 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21069 * @cfg {String} trigger click || hover (or false to trigger manually)
21070 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21071 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21072 * - if false and it has a 'parent' then it will be automatically added to that element
21073 * - if string - Roo.get will be called
21074 * @cfg {Number} delay - delay before showing
21077 * Create a new Popover
21078 * @param {Object} config The config object
21081 Roo.bootstrap.Popover = function(config){
21082 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21088 * After the popover show
21090 * @param {Roo.bootstrap.Popover} this
21095 * After the popover hide
21097 * @param {Roo.bootstrap.Popover} this
21103 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21108 placement : 'right',
21109 trigger : 'hover', // hover
21115 can_build_overlaid : false,
21117 maskEl : false, // the mask element
21120 alignEl : false, // when show is called with an element - this get's stored.
21122 getChildContainer : function()
21124 return this.contentEl;
21127 getPopoverHeader : function()
21129 this.title = true; // flag not to hide it..
21130 this.headerEl.addClass('p-0');
21131 return this.headerEl
21135 getAutoCreate : function(){
21138 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21139 style: 'display:block',
21145 cls : 'popover-inner ',
21149 cls: 'popover-title popover-header',
21150 html : this.title === false ? '' : this.title
21153 cls : 'popover-content popover-body ' + (this.cls || ''),
21154 html : this.html || ''
21165 * @param {string} the title
21167 setTitle: function(str)
21171 this.headerEl.dom.innerHTML = str;
21176 * @param {string} the body content
21178 setContent: function(str)
21181 if (this.contentEl) {
21182 this.contentEl.dom.innerHTML = str;
21186 // as it get's added to the bottom of the page.
21187 onRender : function(ct, position)
21189 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21194 var cfg = Roo.apply({}, this.getAutoCreate());
21198 cfg.cls += ' ' + this.cls;
21201 cfg.style = this.style;
21203 //Roo.log("adding to ");
21204 this.el = Roo.get(document.body).createChild(cfg, position);
21205 // Roo.log(this.el);
21208 this.contentEl = this.el.select('.popover-content',true).first();
21209 this.headerEl = this.el.select('.popover-title',true).first();
21212 if(typeof(this.items) != 'undefined'){
21213 var items = this.items;
21216 for(var i =0;i < items.length;i++) {
21217 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21221 this.items = nitems;
21223 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21224 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21231 resizeMask : function()
21233 this.maskEl.setSize(
21234 Roo.lib.Dom.getViewWidth(true),
21235 Roo.lib.Dom.getViewHeight(true)
21239 initEvents : function()
21243 Roo.bootstrap.Popover.register(this);
21246 this.arrowEl = this.el.select('.arrow',true).first();
21247 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21248 this.el.enableDisplayMode('block');
21252 if (this.over === false && !this.parent()) {
21255 if (this.triggers === false) {
21260 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21261 var triggers = this.trigger ? this.trigger.split(' ') : [];
21262 Roo.each(triggers, function(trigger) {
21264 if (trigger == 'click') {
21265 on_el.on('click', this.toggle, this);
21266 } else if (trigger != 'manual') {
21267 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21268 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21270 on_el.on(eventIn ,this.enter, this);
21271 on_el.on(eventOut, this.leave, this);
21281 toggle : function () {
21282 this.hoverState == 'in' ? this.leave() : this.enter();
21285 enter : function () {
21287 clearTimeout(this.timeout);
21289 this.hoverState = 'in';
21291 if (!this.delay || !this.delay.show) {
21296 this.timeout = setTimeout(function () {
21297 if (_t.hoverState == 'in') {
21300 }, this.delay.show)
21303 leave : function() {
21304 clearTimeout(this.timeout);
21306 this.hoverState = 'out';
21308 if (!this.delay || !this.delay.hide) {
21313 this.timeout = setTimeout(function () {
21314 if (_t.hoverState == 'out') {
21317 }, this.delay.hide)
21321 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21322 * @param {string} (left|right|top|bottom) position
21324 show : function (on_el, placement)
21326 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21327 on_el = on_el || false; // default to false
21330 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21331 on_el = this.parent().el;
21332 } else if (this.over) {
21333 on_el = Roo.get(this.over);
21338 this.alignEl = Roo.get( on_el );
21341 this.render(document.body);
21347 if (this.title === false) {
21348 this.headerEl.hide();
21353 this.el.dom.style.display = 'block';
21356 if (this.alignEl) {
21357 this.updatePosition(this.placement, true);
21360 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21361 var es = this.el.getSize();
21362 var x = Roo.lib.Dom.getViewWidth()/2;
21363 var y = Roo.lib.Dom.getViewHeight()/2;
21364 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21369 //var arrow = this.el.select('.arrow',true).first();
21370 //arrow.set(align[2],
21372 this.el.addClass('in');
21376 this.hoverState = 'in';
21379 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21380 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21381 this.maskEl.dom.style.display = 'block';
21382 this.maskEl.addClass('show');
21384 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21386 this.fireEvent('show', this);
21390 * fire this manually after loading a grid in the table for example
21391 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21392 * @param {Boolean} try and move it if we cant get right position.
21394 updatePosition : function(placement, try_move)
21396 // allow for calling with no parameters
21397 placement = placement ? placement : this.placement;
21398 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21400 this.el.removeClass([
21401 'fade','top','bottom', 'left', 'right','in',
21402 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21404 this.el.addClass(placement + ' bs-popover-' + placement);
21406 if (!this.alignEl ) {
21410 switch (placement) {
21412 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21413 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21414 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21415 //normal display... or moved up/down.
21416 this.el.setXY(offset);
21417 var xy = this.alignEl.getAnchorXY('tr', false);
21419 this.arrowEl.setXY(xy);
21422 // continue through...
21423 return this.updatePosition('left', false);
21427 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21428 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21429 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21430 //normal display... or moved up/down.
21431 this.el.setXY(offset);
21432 var xy = this.alignEl.getAnchorXY('tl', false);
21433 xy[0]-=10;xy[1]+=5; // << fix me
21434 this.arrowEl.setXY(xy);
21438 return this.updatePosition('right', false);
21441 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21442 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21443 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21444 //normal display... or moved up/down.
21445 this.el.setXY(offset);
21446 var xy = this.alignEl.getAnchorXY('t', false);
21447 xy[1]-=10; // << fix me
21448 this.arrowEl.setXY(xy);
21452 return this.updatePosition('bottom', false);
21455 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21456 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21457 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21458 //normal display... or moved up/down.
21459 this.el.setXY(offset);
21460 var xy = this.alignEl.getAnchorXY('b', false);
21461 xy[1]+=2; // << fix me
21462 this.arrowEl.setXY(xy);
21466 return this.updatePosition('top', false);
21477 this.el.setXY([0,0]);
21478 this.el.removeClass('in');
21480 this.hoverState = null;
21481 this.maskEl.hide(); // always..
21482 this.fireEvent('hide', this);
21488 Roo.apply(Roo.bootstrap.Popover, {
21491 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21492 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21493 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21494 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21499 clickHander : false,
21503 onMouseDown : function(e)
21505 if (this.popups.length && !e.getTarget(".roo-popover")) {
21506 /// what is nothing is showing..
21515 register : function(popup)
21517 if (!Roo.bootstrap.Popover.clickHandler) {
21518 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21520 // hide other popups.
21521 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21522 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21523 this.hideAll(); //<< why?
21524 //this.popups.push(popup);
21526 hideAll : function()
21528 this.popups.forEach(function(p) {
21532 onShow : function() {
21533 Roo.bootstrap.Popover.popups.push(this);
21535 onHide : function() {
21536 Roo.bootstrap.Popover.popups.remove(this);
21542 * Card header - holder for the card header elements.
21547 * @class Roo.bootstrap.PopoverNav
21548 * @extends Roo.bootstrap.NavGroup
21549 * Bootstrap Popover header navigation class
21551 * Create a new Popover Header Navigation
21552 * @param {Object} config The config object
21555 Roo.bootstrap.PopoverNav = function(config){
21556 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21559 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21562 container_method : 'getPopoverHeader'
21580 * @class Roo.bootstrap.Progress
21581 * @extends Roo.bootstrap.Component
21582 * Bootstrap Progress class
21583 * @cfg {Boolean} striped striped of the progress bar
21584 * @cfg {Boolean} active animated of the progress bar
21588 * Create a new Progress
21589 * @param {Object} config The config object
21592 Roo.bootstrap.Progress = function(config){
21593 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21596 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21601 getAutoCreate : function(){
21609 cfg.cls += ' progress-striped';
21613 cfg.cls += ' active';
21632 * @class Roo.bootstrap.ProgressBar
21633 * @extends Roo.bootstrap.Component
21634 * Bootstrap ProgressBar class
21635 * @cfg {Number} aria_valuenow aria-value now
21636 * @cfg {Number} aria_valuemin aria-value min
21637 * @cfg {Number} aria_valuemax aria-value max
21638 * @cfg {String} label label for the progress bar
21639 * @cfg {String} panel (success | info | warning | danger )
21640 * @cfg {String} role role of the progress bar
21641 * @cfg {String} sr_only text
21645 * Create a new ProgressBar
21646 * @param {Object} config The config object
21649 Roo.bootstrap.ProgressBar = function(config){
21650 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21653 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21657 aria_valuemax : 100,
21663 getAutoCreate : function()
21668 cls: 'progress-bar',
21669 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21681 cfg.role = this.role;
21684 if(this.aria_valuenow){
21685 cfg['aria-valuenow'] = this.aria_valuenow;
21688 if(this.aria_valuemin){
21689 cfg['aria-valuemin'] = this.aria_valuemin;
21692 if(this.aria_valuemax){
21693 cfg['aria-valuemax'] = this.aria_valuemax;
21696 if(this.label && !this.sr_only){
21697 cfg.html = this.label;
21701 cfg.cls += ' progress-bar-' + this.panel;
21707 update : function(aria_valuenow)
21709 this.aria_valuenow = aria_valuenow;
21711 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21726 * @class Roo.bootstrap.TabGroup
21727 * @extends Roo.bootstrap.Column
21728 * Bootstrap Column class
21729 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21730 * @cfg {Boolean} carousel true to make the group behave like a carousel
21731 * @cfg {Boolean} bullets show bullets for the panels
21732 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21733 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21734 * @cfg {Boolean} showarrow (true|false) show arrow default true
21737 * Create a new TabGroup
21738 * @param {Object} config The config object
21741 Roo.bootstrap.TabGroup = function(config){
21742 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21744 this.navId = Roo.id();
21747 Roo.bootstrap.TabGroup.register(this);
21751 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21754 transition : false,
21759 slideOnTouch : false,
21762 getAutoCreate : function()
21764 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21766 cfg.cls += ' tab-content';
21768 if (this.carousel) {
21769 cfg.cls += ' carousel slide';
21772 cls : 'carousel-inner',
21776 if(this.bullets && !Roo.isTouch){
21779 cls : 'carousel-bullets',
21783 if(this.bullets_cls){
21784 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21791 cfg.cn[0].cn.push(bullets);
21794 if(this.showarrow){
21795 cfg.cn[0].cn.push({
21797 class : 'carousel-arrow',
21801 class : 'carousel-prev',
21805 class : 'fa fa-chevron-left'
21811 class : 'carousel-next',
21815 class : 'fa fa-chevron-right'
21828 initEvents: function()
21830 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21831 // this.el.on("touchstart", this.onTouchStart, this);
21834 if(this.autoslide){
21837 this.slideFn = window.setInterval(function() {
21838 _this.showPanelNext();
21842 if(this.showarrow){
21843 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21844 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21850 // onTouchStart : function(e, el, o)
21852 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21856 // this.showPanelNext();
21860 getChildContainer : function()
21862 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21866 * register a Navigation item
21867 * @param {Roo.bootstrap.NavItem} the navitem to add
21869 register : function(item)
21871 this.tabs.push( item);
21872 item.navId = this.navId; // not really needed..
21877 getActivePanel : function()
21880 Roo.each(this.tabs, function(t) {
21890 getPanelByName : function(n)
21893 Roo.each(this.tabs, function(t) {
21894 if (t.tabId == n) {
21902 indexOfPanel : function(p)
21905 Roo.each(this.tabs, function(t,i) {
21906 if (t.tabId == p.tabId) {
21915 * show a specific panel
21916 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21917 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21919 showPanel : function (pan)
21921 if(this.transition || typeof(pan) == 'undefined'){
21922 Roo.log("waiting for the transitionend");
21926 if (typeof(pan) == 'number') {
21927 pan = this.tabs[pan];
21930 if (typeof(pan) == 'string') {
21931 pan = this.getPanelByName(pan);
21934 var cur = this.getActivePanel();
21937 Roo.log('pan or acitve pan is undefined');
21941 if (pan.tabId == this.getActivePanel().tabId) {
21945 if (false === cur.fireEvent('beforedeactivate')) {
21949 if(this.bullets > 0 && !Roo.isTouch){
21950 this.setActiveBullet(this.indexOfPanel(pan));
21953 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21955 //class="carousel-item carousel-item-next carousel-item-left"
21957 this.transition = true;
21958 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21959 var lr = dir == 'next' ? 'left' : 'right';
21960 pan.el.addClass(dir); // or prev
21961 pan.el.addClass('carousel-item-' + dir); // or prev
21962 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21963 cur.el.addClass(lr); // or right
21964 pan.el.addClass(lr);
21965 cur.el.addClass('carousel-item-' +lr); // or right
21966 pan.el.addClass('carousel-item-' +lr);
21970 cur.el.on('transitionend', function() {
21971 Roo.log("trans end?");
21973 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21974 pan.setActive(true);
21976 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21977 cur.setActive(false);
21979 _this.transition = false;
21981 }, this, { single: true } );
21986 cur.setActive(false);
21987 pan.setActive(true);
21992 showPanelNext : function()
21994 var i = this.indexOfPanel(this.getActivePanel());
21996 if (i >= this.tabs.length - 1 && !this.autoslide) {
22000 if (i >= this.tabs.length - 1 && this.autoslide) {
22004 this.showPanel(this.tabs[i+1]);
22007 showPanelPrev : function()
22009 var i = this.indexOfPanel(this.getActivePanel());
22011 if (i < 1 && !this.autoslide) {
22015 if (i < 1 && this.autoslide) {
22016 i = this.tabs.length;
22019 this.showPanel(this.tabs[i-1]);
22023 addBullet: function()
22025 if(!this.bullets || Roo.isTouch){
22028 var ctr = this.el.select('.carousel-bullets',true).first();
22029 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22030 var bullet = ctr.createChild({
22031 cls : 'bullet bullet-' + i
22032 },ctr.dom.lastChild);
22037 bullet.on('click', (function(e, el, o, ii, t){
22039 e.preventDefault();
22041 this.showPanel(ii);
22043 if(this.autoslide && this.slideFn){
22044 clearInterval(this.slideFn);
22045 this.slideFn = window.setInterval(function() {
22046 _this.showPanelNext();
22050 }).createDelegate(this, [i, bullet], true));
22055 setActiveBullet : function(i)
22061 Roo.each(this.el.select('.bullet', true).elements, function(el){
22062 el.removeClass('selected');
22065 var bullet = this.el.select('.bullet-' + i, true).first();
22071 bullet.addClass('selected');
22082 Roo.apply(Roo.bootstrap.TabGroup, {
22086 * register a Navigation Group
22087 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22089 register : function(navgrp)
22091 this.groups[navgrp.navId] = navgrp;
22095 * fetch a Navigation Group based on the navigation ID
22096 * if one does not exist , it will get created.
22097 * @param {string} the navgroup to add
22098 * @returns {Roo.bootstrap.NavGroup} the navgroup
22100 get: function(navId) {
22101 if (typeof(this.groups[navId]) == 'undefined') {
22102 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22104 return this.groups[navId] ;
22119 * @class Roo.bootstrap.TabPanel
22120 * @extends Roo.bootstrap.Component
22121 * Bootstrap TabPanel class
22122 * @cfg {Boolean} active panel active
22123 * @cfg {String} html panel content
22124 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22125 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22126 * @cfg {String} href click to link..
22127 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22131 * Create a new TabPanel
22132 * @param {Object} config The config object
22135 Roo.bootstrap.TabPanel = function(config){
22136 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22140 * Fires when the active status changes
22141 * @param {Roo.bootstrap.TabPanel} this
22142 * @param {Boolean} state the new state
22147 * @event beforedeactivate
22148 * Fires before a tab is de-activated - can be used to do validation on a form.
22149 * @param {Roo.bootstrap.TabPanel} this
22150 * @return {Boolean} false if there is an error
22153 'beforedeactivate': true
22156 this.tabId = this.tabId || Roo.id();
22160 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22167 touchSlide : false,
22168 getAutoCreate : function(){
22173 // item is needed for carousel - not sure if it has any effect otherwise
22174 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22175 html: this.html || ''
22179 cfg.cls += ' active';
22183 cfg.tabId = this.tabId;
22191 initEvents: function()
22193 var p = this.parent();
22195 this.navId = this.navId || p.navId;
22197 if (typeof(this.navId) != 'undefined') {
22198 // not really needed.. but just in case.. parent should be a NavGroup.
22199 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22203 var i = tg.tabs.length - 1;
22205 if(this.active && tg.bullets > 0 && i < tg.bullets){
22206 tg.setActiveBullet(i);
22210 this.el.on('click', this.onClick, this);
22212 if(Roo.isTouch && this.touchSlide){
22213 this.el.on("touchstart", this.onTouchStart, this);
22214 this.el.on("touchmove", this.onTouchMove, this);
22215 this.el.on("touchend", this.onTouchEnd, this);
22220 onRender : function(ct, position)
22222 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22225 setActive : function(state)
22227 Roo.log("panel - set active " + this.tabId + "=" + state);
22229 this.active = state;
22231 this.el.removeClass('active');
22233 } else if (!this.el.hasClass('active')) {
22234 this.el.addClass('active');
22237 this.fireEvent('changed', this, state);
22240 onClick : function(e)
22242 e.preventDefault();
22244 if(!this.href.length){
22248 window.location.href = this.href;
22257 onTouchStart : function(e)
22259 this.swiping = false;
22261 this.startX = e.browserEvent.touches[0].clientX;
22262 this.startY = e.browserEvent.touches[0].clientY;
22265 onTouchMove : function(e)
22267 this.swiping = true;
22269 this.endX = e.browserEvent.touches[0].clientX;
22270 this.endY = e.browserEvent.touches[0].clientY;
22273 onTouchEnd : function(e)
22280 var tabGroup = this.parent();
22282 if(this.endX > this.startX){ // swiping right
22283 tabGroup.showPanelPrev();
22287 if(this.startX > this.endX){ // swiping left
22288 tabGroup.showPanelNext();
22307 * @class Roo.bootstrap.DateField
22308 * @extends Roo.bootstrap.Input
22309 * Bootstrap DateField class
22310 * @cfg {Number} weekStart default 0
22311 * @cfg {String} viewMode default empty, (months|years)
22312 * @cfg {String} minViewMode default empty, (months|years)
22313 * @cfg {Number} startDate default -Infinity
22314 * @cfg {Number} endDate default Infinity
22315 * @cfg {Boolean} todayHighlight default false
22316 * @cfg {Boolean} todayBtn default false
22317 * @cfg {Boolean} calendarWeeks default false
22318 * @cfg {Object} daysOfWeekDisabled default empty
22319 * @cfg {Boolean} singleMode default false (true | false)
22321 * @cfg {Boolean} keyboardNavigation default true
22322 * @cfg {String} language default en
22325 * Create a new DateField
22326 * @param {Object} config The config object
22329 Roo.bootstrap.DateField = function(config){
22330 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22334 * Fires when this field show.
22335 * @param {Roo.bootstrap.DateField} this
22336 * @param {Mixed} date The date value
22341 * Fires when this field hide.
22342 * @param {Roo.bootstrap.DateField} this
22343 * @param {Mixed} date The date value
22348 * Fires when select a date.
22349 * @param {Roo.bootstrap.DateField} this
22350 * @param {Mixed} date The date value
22354 * @event beforeselect
22355 * Fires when before select a date.
22356 * @param {Roo.bootstrap.DateField} this
22357 * @param {Mixed} date The date value
22359 beforeselect : true
22363 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22366 * @cfg {String} format
22367 * The default date format string which can be overriden for localization support. The format must be
22368 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22372 * @cfg {String} altFormats
22373 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22374 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22376 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22384 todayHighlight : false,
22390 keyboardNavigation: true,
22392 calendarWeeks: false,
22394 startDate: -Infinity,
22398 daysOfWeekDisabled: [],
22402 singleMode : false,
22404 UTCDate: function()
22406 return new Date(Date.UTC.apply(Date, arguments));
22409 UTCToday: function()
22411 var today = new Date();
22412 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22415 getDate: function() {
22416 var d = this.getUTCDate();
22417 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22420 getUTCDate: function() {
22424 setDate: function(d) {
22425 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22428 setUTCDate: function(d) {
22430 this.setValue(this.formatDate(this.date));
22433 onRender: function(ct, position)
22436 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22438 this.language = this.language || 'en';
22439 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22440 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22442 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22443 this.format = this.format || 'm/d/y';
22444 this.isInline = false;
22445 this.isInput = true;
22446 this.component = this.el.select('.add-on', true).first() || false;
22447 this.component = (this.component && this.component.length === 0) ? false : this.component;
22448 this.hasInput = this.component && this.inputEl().length;
22450 if (typeof(this.minViewMode === 'string')) {
22451 switch (this.minViewMode) {
22453 this.minViewMode = 1;
22456 this.minViewMode = 2;
22459 this.minViewMode = 0;
22464 if (typeof(this.viewMode === 'string')) {
22465 switch (this.viewMode) {
22478 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22480 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22482 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22484 this.picker().on('mousedown', this.onMousedown, this);
22485 this.picker().on('click', this.onClick, this);
22487 this.picker().addClass('datepicker-dropdown');
22489 this.startViewMode = this.viewMode;
22491 if(this.singleMode){
22492 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22493 v.setVisibilityMode(Roo.Element.DISPLAY);
22497 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22498 v.setStyle('width', '189px');
22502 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22503 if(!this.calendarWeeks){
22508 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22509 v.attr('colspan', function(i, val){
22510 return parseInt(val) + 1;
22515 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22517 this.setStartDate(this.startDate);
22518 this.setEndDate(this.endDate);
22520 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22527 if(this.isInline) {
22532 picker : function()
22534 return this.pickerEl;
22535 // return this.el.select('.datepicker', true).first();
22538 fillDow: function()
22540 var dowCnt = this.weekStart;
22549 if(this.calendarWeeks){
22557 while (dowCnt < this.weekStart + 7) {
22561 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22565 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22568 fillMonths: function()
22571 var months = this.picker().select('>.datepicker-months td', true).first();
22573 months.dom.innerHTML = '';
22579 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22582 months.createChild(month);
22589 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;
22591 if (this.date < this.startDate) {
22592 this.viewDate = new Date(this.startDate);
22593 } else if (this.date > this.endDate) {
22594 this.viewDate = new Date(this.endDate);
22596 this.viewDate = new Date(this.date);
22604 var d = new Date(this.viewDate),
22605 year = d.getUTCFullYear(),
22606 month = d.getUTCMonth(),
22607 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22608 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22609 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22610 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22611 currentDate = this.date && this.date.valueOf(),
22612 today = this.UTCToday();
22614 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22616 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22618 // this.picker.select('>tfoot th.today').
22619 // .text(dates[this.language].today)
22620 // .toggle(this.todayBtn !== false);
22622 this.updateNavArrows();
22625 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22627 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22629 prevMonth.setUTCDate(day);
22631 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22633 var nextMonth = new Date(prevMonth);
22635 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22637 nextMonth = nextMonth.valueOf();
22639 var fillMonths = false;
22641 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22643 while(prevMonth.valueOf() <= nextMonth) {
22646 if (prevMonth.getUTCDay() === this.weekStart) {
22648 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22656 if(this.calendarWeeks){
22657 // ISO 8601: First week contains first thursday.
22658 // ISO also states week starts on Monday, but we can be more abstract here.
22660 // Start of current week: based on weekstart/current date
22661 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22662 // Thursday of this week
22663 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22664 // First Thursday of year, year from thursday
22665 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22666 // Calendar week: ms between thursdays, div ms per day, div 7 days
22667 calWeek = (th - yth) / 864e5 / 7 + 1;
22669 fillMonths.cn.push({
22677 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22679 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22682 if (this.todayHighlight &&
22683 prevMonth.getUTCFullYear() == today.getFullYear() &&
22684 prevMonth.getUTCMonth() == today.getMonth() &&
22685 prevMonth.getUTCDate() == today.getDate()) {
22686 clsName += ' today';
22689 if (currentDate && prevMonth.valueOf() === currentDate) {
22690 clsName += ' active';
22693 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22694 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22695 clsName += ' disabled';
22698 fillMonths.cn.push({
22700 cls: 'day ' + clsName,
22701 html: prevMonth.getDate()
22704 prevMonth.setDate(prevMonth.getDate()+1);
22707 var currentYear = this.date && this.date.getUTCFullYear();
22708 var currentMonth = this.date && this.date.getUTCMonth();
22710 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22712 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22713 v.removeClass('active');
22715 if(currentYear === year && k === currentMonth){
22716 v.addClass('active');
22719 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22720 v.addClass('disabled');
22726 year = parseInt(year/10, 10) * 10;
22728 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22730 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22733 for (var i = -1; i < 11; i++) {
22734 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22736 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22744 showMode: function(dir)
22747 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22750 Roo.each(this.picker().select('>div',true).elements, function(v){
22751 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22754 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22759 if(this.isInline) {
22763 this.picker().removeClass(['bottom', 'top']);
22765 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22767 * place to the top of element!
22771 this.picker().addClass('top');
22772 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22777 this.picker().addClass('bottom');
22779 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22782 parseDate : function(value)
22784 if(!value || value instanceof Date){
22787 var v = Date.parseDate(value, this.format);
22788 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22789 v = Date.parseDate(value, 'Y-m-d');
22791 if(!v && this.altFormats){
22792 if(!this.altFormatsArray){
22793 this.altFormatsArray = this.altFormats.split("|");
22795 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22796 v = Date.parseDate(value, this.altFormatsArray[i]);
22802 formatDate : function(date, fmt)
22804 return (!date || !(date instanceof Date)) ?
22805 date : date.dateFormat(fmt || this.format);
22808 onFocus : function()
22810 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22814 onBlur : function()
22816 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22818 var d = this.inputEl().getValue();
22825 showPopup : function()
22827 this.picker().show();
22831 this.fireEvent('showpopup', this, this.date);
22834 hidePopup : function()
22836 if(this.isInline) {
22839 this.picker().hide();
22840 this.viewMode = this.startViewMode;
22843 this.fireEvent('hidepopup', this, this.date);
22847 onMousedown: function(e)
22849 e.stopPropagation();
22850 e.preventDefault();
22855 Roo.bootstrap.DateField.superclass.keyup.call(this);
22859 setValue: function(v)
22861 if(this.fireEvent('beforeselect', this, v) !== false){
22862 var d = new Date(this.parseDate(v) ).clearTime();
22864 if(isNaN(d.getTime())){
22865 this.date = this.viewDate = '';
22866 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22870 v = this.formatDate(d);
22872 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22874 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22878 this.fireEvent('select', this, this.date);
22882 getValue: function()
22884 return this.formatDate(this.date);
22887 fireKey: function(e)
22889 if (!this.picker().isVisible()){
22890 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22896 var dateChanged = false,
22898 newDate, newViewDate;
22903 e.preventDefault();
22907 if (!this.keyboardNavigation) {
22910 dir = e.keyCode == 37 ? -1 : 1;
22913 newDate = this.moveYear(this.date, dir);
22914 newViewDate = this.moveYear(this.viewDate, dir);
22915 } else if (e.shiftKey){
22916 newDate = this.moveMonth(this.date, dir);
22917 newViewDate = this.moveMonth(this.viewDate, dir);
22919 newDate = new Date(this.date);
22920 newDate.setUTCDate(this.date.getUTCDate() + dir);
22921 newViewDate = new Date(this.viewDate);
22922 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22924 if (this.dateWithinRange(newDate)){
22925 this.date = newDate;
22926 this.viewDate = newViewDate;
22927 this.setValue(this.formatDate(this.date));
22929 e.preventDefault();
22930 dateChanged = true;
22935 if (!this.keyboardNavigation) {
22938 dir = e.keyCode == 38 ? -1 : 1;
22940 newDate = this.moveYear(this.date, dir);
22941 newViewDate = this.moveYear(this.viewDate, dir);
22942 } else if (e.shiftKey){
22943 newDate = this.moveMonth(this.date, dir);
22944 newViewDate = this.moveMonth(this.viewDate, dir);
22946 newDate = new Date(this.date);
22947 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22948 newViewDate = new Date(this.viewDate);
22949 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22951 if (this.dateWithinRange(newDate)){
22952 this.date = newDate;
22953 this.viewDate = newViewDate;
22954 this.setValue(this.formatDate(this.date));
22956 e.preventDefault();
22957 dateChanged = true;
22961 this.setValue(this.formatDate(this.date));
22963 e.preventDefault();
22966 this.setValue(this.formatDate(this.date));
22980 onClick: function(e)
22982 e.stopPropagation();
22983 e.preventDefault();
22985 var target = e.getTarget();
22987 if(target.nodeName.toLowerCase() === 'i'){
22988 target = Roo.get(target).dom.parentNode;
22991 var nodeName = target.nodeName;
22992 var className = target.className;
22993 var html = target.innerHTML;
22994 //Roo.log(nodeName);
22996 switch(nodeName.toLowerCase()) {
22998 switch(className) {
23004 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23005 switch(this.viewMode){
23007 this.viewDate = this.moveMonth(this.viewDate, dir);
23011 this.viewDate = this.moveYear(this.viewDate, dir);
23017 var date = new Date();
23018 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23020 this.setValue(this.formatDate(this.date));
23027 if (className.indexOf('disabled') < 0) {
23028 if (!this.viewDate) {
23029 this.viewDate = new Date();
23031 this.viewDate.setUTCDate(1);
23032 if (className.indexOf('month') > -1) {
23033 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23035 var year = parseInt(html, 10) || 0;
23036 this.viewDate.setUTCFullYear(year);
23040 if(this.singleMode){
23041 this.setValue(this.formatDate(this.viewDate));
23052 //Roo.log(className);
23053 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23054 var day = parseInt(html, 10) || 1;
23055 var year = (this.viewDate || new Date()).getUTCFullYear(),
23056 month = (this.viewDate || new Date()).getUTCMonth();
23058 if (className.indexOf('old') > -1) {
23065 } else if (className.indexOf('new') > -1) {
23073 //Roo.log([year,month,day]);
23074 this.date = this.UTCDate(year, month, day,0,0,0,0);
23075 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23077 //Roo.log(this.formatDate(this.date));
23078 this.setValue(this.formatDate(this.date));
23085 setStartDate: function(startDate)
23087 this.startDate = startDate || -Infinity;
23088 if (this.startDate !== -Infinity) {
23089 this.startDate = this.parseDate(this.startDate);
23092 this.updateNavArrows();
23095 setEndDate: function(endDate)
23097 this.endDate = endDate || Infinity;
23098 if (this.endDate !== Infinity) {
23099 this.endDate = this.parseDate(this.endDate);
23102 this.updateNavArrows();
23105 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23107 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23108 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23109 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23111 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23112 return parseInt(d, 10);
23115 this.updateNavArrows();
23118 updateNavArrows: function()
23120 if(this.singleMode){
23124 var d = new Date(this.viewDate),
23125 year = d.getUTCFullYear(),
23126 month = d.getUTCMonth();
23128 Roo.each(this.picker().select('.prev', true).elements, function(v){
23130 switch (this.viewMode) {
23133 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23139 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23146 Roo.each(this.picker().select('.next', true).elements, function(v){
23148 switch (this.viewMode) {
23151 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23157 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23165 moveMonth: function(date, dir)
23170 var new_date = new Date(date.valueOf()),
23171 day = new_date.getUTCDate(),
23172 month = new_date.getUTCMonth(),
23173 mag = Math.abs(dir),
23175 dir = dir > 0 ? 1 : -1;
23178 // If going back one month, make sure month is not current month
23179 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23181 return new_date.getUTCMonth() == month;
23183 // If going forward one month, make sure month is as expected
23184 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23186 return new_date.getUTCMonth() != new_month;
23188 new_month = month + dir;
23189 new_date.setUTCMonth(new_month);
23190 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23191 if (new_month < 0 || new_month > 11) {
23192 new_month = (new_month + 12) % 12;
23195 // For magnitudes >1, move one month at a time...
23196 for (var i=0; i<mag; i++) {
23197 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23198 new_date = this.moveMonth(new_date, dir);
23200 // ...then reset the day, keeping it in the new month
23201 new_month = new_date.getUTCMonth();
23202 new_date.setUTCDate(day);
23204 return new_month != new_date.getUTCMonth();
23207 // Common date-resetting loop -- if date is beyond end of month, make it
23210 new_date.setUTCDate(--day);
23211 new_date.setUTCMonth(new_month);
23216 moveYear: function(date, dir)
23218 return this.moveMonth(date, dir*12);
23221 dateWithinRange: function(date)
23223 return date >= this.startDate && date <= this.endDate;
23229 this.picker().remove();
23232 validateValue : function(value)
23234 if(this.getVisibilityEl().hasClass('hidden')){
23238 if(value.length < 1) {
23239 if(this.allowBlank){
23245 if(value.length < this.minLength){
23248 if(value.length > this.maxLength){
23252 var vt = Roo.form.VTypes;
23253 if(!vt[this.vtype](value, this)){
23257 if(typeof this.validator == "function"){
23258 var msg = this.validator(value);
23264 if(this.regex && !this.regex.test(value)){
23268 if(typeof(this.parseDate(value)) == 'undefined'){
23272 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23276 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23286 this.date = this.viewDate = '';
23288 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23293 Roo.apply(Roo.bootstrap.DateField, {
23304 html: '<i class="fa fa-arrow-left"/>'
23314 html: '<i class="fa fa-arrow-right"/>'
23356 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23357 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23358 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23359 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23360 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23373 navFnc: 'FullYear',
23378 navFnc: 'FullYear',
23383 Roo.apply(Roo.bootstrap.DateField, {
23387 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23391 cls: 'datepicker-days',
23395 cls: 'table-condensed',
23397 Roo.bootstrap.DateField.head,
23401 Roo.bootstrap.DateField.footer
23408 cls: 'datepicker-months',
23412 cls: 'table-condensed',
23414 Roo.bootstrap.DateField.head,
23415 Roo.bootstrap.DateField.content,
23416 Roo.bootstrap.DateField.footer
23423 cls: 'datepicker-years',
23427 cls: 'table-condensed',
23429 Roo.bootstrap.DateField.head,
23430 Roo.bootstrap.DateField.content,
23431 Roo.bootstrap.DateField.footer
23450 * @class Roo.bootstrap.TimeField
23451 * @extends Roo.bootstrap.Input
23452 * Bootstrap DateField class
23456 * Create a new TimeField
23457 * @param {Object} config The config object
23460 Roo.bootstrap.TimeField = function(config){
23461 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23465 * Fires when this field show.
23466 * @param {Roo.bootstrap.DateField} thisthis
23467 * @param {Mixed} date The date value
23472 * Fires when this field hide.
23473 * @param {Roo.bootstrap.DateField} this
23474 * @param {Mixed} date The date value
23479 * Fires when select a date.
23480 * @param {Roo.bootstrap.DateField} this
23481 * @param {Mixed} date The date value
23487 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23490 * @cfg {String} format
23491 * The default time format string which can be overriden for localization support. The format must be
23492 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23496 getAutoCreate : function()
23498 this.after = '<i class="fa far fa-clock"></i>';
23499 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23503 onRender: function(ct, position)
23506 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23508 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23510 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23512 this.pop = this.picker().select('>.datepicker-time',true).first();
23513 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23515 this.picker().on('mousedown', this.onMousedown, this);
23516 this.picker().on('click', this.onClick, this);
23518 this.picker().addClass('datepicker-dropdown');
23523 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23524 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23525 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23526 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23527 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23528 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23532 fireKey: function(e){
23533 if (!this.picker().isVisible()){
23534 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23540 e.preventDefault();
23548 this.onTogglePeriod();
23551 this.onIncrementMinutes();
23554 this.onDecrementMinutes();
23563 onClick: function(e) {
23564 e.stopPropagation();
23565 e.preventDefault();
23568 picker : function()
23570 return this.pickerEl;
23573 fillTime: function()
23575 var time = this.pop.select('tbody', true).first();
23577 time.dom.innerHTML = '';
23592 cls: 'hours-up fa fas fa-chevron-up'
23612 cls: 'minutes-up fa fas fa-chevron-up'
23633 cls: 'timepicker-hour',
23648 cls: 'timepicker-minute',
23663 cls: 'btn btn-primary period',
23685 cls: 'hours-down fa fas fa-chevron-down'
23705 cls: 'minutes-down fa fas fa-chevron-down'
23723 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23730 var hours = this.time.getHours();
23731 var minutes = this.time.getMinutes();
23744 hours = hours - 12;
23748 hours = '0' + hours;
23752 minutes = '0' + minutes;
23755 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23756 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23757 this.pop.select('button', true).first().dom.innerHTML = period;
23763 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23765 var cls = ['bottom'];
23767 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23774 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23778 //this.picker().setXY(20000,20000);
23779 this.picker().addClass(cls.join('-'));
23783 Roo.each(cls, function(c){
23788 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23789 //_this.picker().setTop(_this.inputEl().getHeight());
23793 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23795 //_this.picker().setTop(0 - _this.picker().getHeight());
23800 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23804 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23812 onFocus : function()
23814 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23818 onBlur : function()
23820 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23826 this.picker().show();
23831 this.fireEvent('show', this, this.date);
23836 this.picker().hide();
23839 this.fireEvent('hide', this, this.date);
23842 setTime : function()
23845 this.setValue(this.time.format(this.format));
23847 this.fireEvent('select', this, this.date);
23852 onMousedown: function(e){
23853 e.stopPropagation();
23854 e.preventDefault();
23857 onIncrementHours: function()
23859 Roo.log('onIncrementHours');
23860 this.time = this.time.add(Date.HOUR, 1);
23865 onDecrementHours: function()
23867 Roo.log('onDecrementHours');
23868 this.time = this.time.add(Date.HOUR, -1);
23872 onIncrementMinutes: function()
23874 Roo.log('onIncrementMinutes');
23875 this.time = this.time.add(Date.MINUTE, 1);
23879 onDecrementMinutes: function()
23881 Roo.log('onDecrementMinutes');
23882 this.time = this.time.add(Date.MINUTE, -1);
23886 onTogglePeriod: function()
23888 Roo.log('onTogglePeriod');
23889 this.time = this.time.add(Date.HOUR, 12);
23897 Roo.apply(Roo.bootstrap.TimeField, {
23901 cls: 'datepicker dropdown-menu',
23905 cls: 'datepicker-time',
23909 cls: 'table-condensed',
23938 cls: 'btn btn-info ok',
23966 * @class Roo.bootstrap.MonthField
23967 * @extends Roo.bootstrap.Input
23968 * Bootstrap MonthField class
23970 * @cfg {String} language default en
23973 * Create a new MonthField
23974 * @param {Object} config The config object
23977 Roo.bootstrap.MonthField = function(config){
23978 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23983 * Fires when this field show.
23984 * @param {Roo.bootstrap.MonthField} this
23985 * @param {Mixed} date The date value
23990 * Fires when this field hide.
23991 * @param {Roo.bootstrap.MonthField} this
23992 * @param {Mixed} date The date value
23997 * Fires when select a date.
23998 * @param {Roo.bootstrap.MonthField} this
23999 * @param {String} oldvalue The old value
24000 * @param {String} newvalue The new value
24006 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24008 onRender: function(ct, position)
24011 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24013 this.language = this.language || 'en';
24014 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24015 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24017 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24018 this.isInline = false;
24019 this.isInput = true;
24020 this.component = this.el.select('.add-on', true).first() || false;
24021 this.component = (this.component && this.component.length === 0) ? false : this.component;
24022 this.hasInput = this.component && this.inputEL().length;
24024 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24026 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24028 this.picker().on('mousedown', this.onMousedown, this);
24029 this.picker().on('click', this.onClick, this);
24031 this.picker().addClass('datepicker-dropdown');
24033 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24034 v.setStyle('width', '189px');
24041 if(this.isInline) {
24047 setValue: function(v, suppressEvent)
24049 var o = this.getValue();
24051 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24055 if(suppressEvent !== true){
24056 this.fireEvent('select', this, o, v);
24061 getValue: function()
24066 onClick: function(e)
24068 e.stopPropagation();
24069 e.preventDefault();
24071 var target = e.getTarget();
24073 if(target.nodeName.toLowerCase() === 'i'){
24074 target = Roo.get(target).dom.parentNode;
24077 var nodeName = target.nodeName;
24078 var className = target.className;
24079 var html = target.innerHTML;
24081 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24085 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24087 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24093 picker : function()
24095 return this.pickerEl;
24098 fillMonths: function()
24101 var months = this.picker().select('>.datepicker-months td', true).first();
24103 months.dom.innerHTML = '';
24109 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24112 months.createChild(month);
24121 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24122 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24125 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24126 e.removeClass('active');
24128 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24129 e.addClass('active');
24136 if(this.isInline) {
24140 this.picker().removeClass(['bottom', 'top']);
24142 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24144 * place to the top of element!
24148 this.picker().addClass('top');
24149 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24154 this.picker().addClass('bottom');
24156 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24159 onFocus : function()
24161 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24165 onBlur : function()
24167 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24169 var d = this.inputEl().getValue();
24178 this.picker().show();
24179 this.picker().select('>.datepicker-months', true).first().show();
24183 this.fireEvent('show', this, this.date);
24188 if(this.isInline) {
24191 this.picker().hide();
24192 this.fireEvent('hide', this, this.date);
24196 onMousedown: function(e)
24198 e.stopPropagation();
24199 e.preventDefault();
24204 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24208 fireKey: function(e)
24210 if (!this.picker().isVisible()){
24211 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24222 e.preventDefault();
24226 dir = e.keyCode == 37 ? -1 : 1;
24228 this.vIndex = this.vIndex + dir;
24230 if(this.vIndex < 0){
24234 if(this.vIndex > 11){
24238 if(isNaN(this.vIndex)){
24242 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24248 dir = e.keyCode == 38 ? -1 : 1;
24250 this.vIndex = this.vIndex + dir * 4;
24252 if(this.vIndex < 0){
24256 if(this.vIndex > 11){
24260 if(isNaN(this.vIndex)){
24264 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24269 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24270 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24274 e.preventDefault();
24277 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24278 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24294 this.picker().remove();
24299 Roo.apply(Roo.bootstrap.MonthField, {
24318 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24319 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24324 Roo.apply(Roo.bootstrap.MonthField, {
24328 cls: 'datepicker dropdown-menu roo-dynamic',
24332 cls: 'datepicker-months',
24336 cls: 'table-condensed',
24338 Roo.bootstrap.DateField.content
24358 * @class Roo.bootstrap.CheckBox
24359 * @extends Roo.bootstrap.Input
24360 * Bootstrap CheckBox class
24362 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24363 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24364 * @cfg {String} boxLabel The text that appears beside the checkbox
24365 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24366 * @cfg {Boolean} checked initnal the element
24367 * @cfg {Boolean} inline inline the element (default false)
24368 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24369 * @cfg {String} tooltip label tooltip
24372 * Create a new CheckBox
24373 * @param {Object} config The config object
24376 Roo.bootstrap.CheckBox = function(config){
24377 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24382 * Fires when the element is checked or unchecked.
24383 * @param {Roo.bootstrap.CheckBox} this This input
24384 * @param {Boolean} checked The new checked value
24389 * Fires when the element is click.
24390 * @param {Roo.bootstrap.CheckBox} this This input
24397 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24399 inputType: 'checkbox',
24408 // checkbox success does not make any sense really..
24413 getAutoCreate : function()
24415 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24421 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24424 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24430 type : this.inputType,
24431 value : this.inputValue,
24432 cls : 'roo-' + this.inputType, //'form-box',
24433 placeholder : this.placeholder || ''
24437 if(this.inputType != 'radio'){
24441 cls : 'roo-hidden-value',
24442 value : this.checked ? this.inputValue : this.valueOff
24447 if (this.weight) { // Validity check?
24448 cfg.cls += " " + this.inputType + "-" + this.weight;
24451 if (this.disabled) {
24452 input.disabled=true;
24456 input.checked = this.checked;
24461 input.name = this.name;
24463 if(this.inputType != 'radio'){
24464 hidden.name = this.name;
24465 input.name = '_hidden_' + this.name;
24470 input.cls += ' input-' + this.size;
24475 ['xs','sm','md','lg'].map(function(size){
24476 if (settings[size]) {
24477 cfg.cls += ' col-' + size + '-' + settings[size];
24481 var inputblock = input;
24483 if (this.before || this.after) {
24486 cls : 'input-group',
24491 inputblock.cn.push({
24493 cls : 'input-group-addon',
24498 inputblock.cn.push(input);
24500 if(this.inputType != 'radio'){
24501 inputblock.cn.push(hidden);
24505 inputblock.cn.push({
24507 cls : 'input-group-addon',
24513 var boxLabelCfg = false;
24519 //'for': id, // box label is handled by onclick - so no for...
24521 html: this.boxLabel
24524 boxLabelCfg.tooltip = this.tooltip;
24530 if (align ==='left' && this.fieldLabel.length) {
24531 // Roo.log("left and has label");
24536 cls : 'control-label',
24537 html : this.fieldLabel
24548 cfg.cn[1].cn.push(boxLabelCfg);
24551 if(this.labelWidth > 12){
24552 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24555 if(this.labelWidth < 13 && this.labelmd == 0){
24556 this.labelmd = this.labelWidth;
24559 if(this.labellg > 0){
24560 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24561 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24564 if(this.labelmd > 0){
24565 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24566 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24569 if(this.labelsm > 0){
24570 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24571 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24574 if(this.labelxs > 0){
24575 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24576 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24579 } else if ( this.fieldLabel.length) {
24580 // Roo.log(" label");
24584 tag: this.boxLabel ? 'span' : 'label',
24586 cls: 'control-label box-input-label',
24587 //cls : 'input-group-addon',
24588 html : this.fieldLabel
24595 cfg.cn.push(boxLabelCfg);
24600 // Roo.log(" no label && no align");
24601 cfg.cn = [ inputblock ] ;
24603 cfg.cn.push(boxLabelCfg);
24611 if(this.inputType != 'radio'){
24612 cfg.cn.push(hidden);
24620 * return the real input element.
24622 inputEl: function ()
24624 return this.el.select('input.roo-' + this.inputType,true).first();
24626 hiddenEl: function ()
24628 return this.el.select('input.roo-hidden-value',true).first();
24631 labelEl: function()
24633 return this.el.select('label.control-label',true).first();
24635 /* depricated... */
24639 return this.labelEl();
24642 boxLabelEl: function()
24644 return this.el.select('label.box-label',true).first();
24647 initEvents : function()
24649 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24651 this.inputEl().on('click', this.onClick, this);
24653 if (this.boxLabel) {
24654 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24657 this.startValue = this.getValue();
24660 Roo.bootstrap.CheckBox.register(this);
24664 onClick : function(e)
24666 if(this.fireEvent('click', this, e) !== false){
24667 this.setChecked(!this.checked);
24672 setChecked : function(state,suppressEvent)
24674 this.startValue = this.getValue();
24676 if(this.inputType == 'radio'){
24678 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24679 e.dom.checked = false;
24682 this.inputEl().dom.checked = true;
24684 this.inputEl().dom.value = this.inputValue;
24686 if(suppressEvent !== true){
24687 this.fireEvent('check', this, true);
24695 this.checked = state;
24697 this.inputEl().dom.checked = state;
24700 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24702 if(suppressEvent !== true){
24703 this.fireEvent('check', this, state);
24709 getValue : function()
24711 if(this.inputType == 'radio'){
24712 return this.getGroupValue();
24715 return this.hiddenEl().dom.value;
24719 getGroupValue : function()
24721 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24725 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24728 setValue : function(v,suppressEvent)
24730 if(this.inputType == 'radio'){
24731 this.setGroupValue(v, suppressEvent);
24735 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24740 setGroupValue : function(v, suppressEvent)
24742 this.startValue = this.getValue();
24744 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24745 e.dom.checked = false;
24747 if(e.dom.value == v){
24748 e.dom.checked = true;
24752 if(suppressEvent !== true){
24753 this.fireEvent('check', this, true);
24761 validate : function()
24763 if(this.getVisibilityEl().hasClass('hidden')){
24769 (this.inputType == 'radio' && this.validateRadio()) ||
24770 (this.inputType == 'checkbox' && this.validateCheckbox())
24776 this.markInvalid();
24780 validateRadio : function()
24782 if(this.getVisibilityEl().hasClass('hidden')){
24786 if(this.allowBlank){
24792 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24793 if(!e.dom.checked){
24805 validateCheckbox : function()
24808 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24809 //return (this.getValue() == this.inputValue) ? true : false;
24812 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24820 for(var i in group){
24821 if(group[i].el.isVisible(true)){
24829 for(var i in group){
24834 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24841 * Mark this field as valid
24843 markValid : function()
24847 this.fireEvent('valid', this);
24849 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24852 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24859 if(this.inputType == 'radio'){
24860 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24861 var fg = e.findParent('.form-group', false, true);
24862 if (Roo.bootstrap.version == 3) {
24863 fg.removeClass([_this.invalidClass, _this.validClass]);
24864 fg.addClass(_this.validClass);
24866 fg.removeClass(['is-valid', 'is-invalid']);
24867 fg.addClass('is-valid');
24875 var fg = this.el.findParent('.form-group', false, true);
24876 if (Roo.bootstrap.version == 3) {
24877 fg.removeClass([this.invalidClass, this.validClass]);
24878 fg.addClass(this.validClass);
24880 fg.removeClass(['is-valid', 'is-invalid']);
24881 fg.addClass('is-valid');
24886 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24892 for(var i in group){
24893 var fg = group[i].el.findParent('.form-group', false, true);
24894 if (Roo.bootstrap.version == 3) {
24895 fg.removeClass([this.invalidClass, this.validClass]);
24896 fg.addClass(this.validClass);
24898 fg.removeClass(['is-valid', 'is-invalid']);
24899 fg.addClass('is-valid');
24905 * Mark this field as invalid
24906 * @param {String} msg The validation message
24908 markInvalid : function(msg)
24910 if(this.allowBlank){
24916 this.fireEvent('invalid', this, msg);
24918 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24921 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24925 label.markInvalid();
24928 if(this.inputType == 'radio'){
24930 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24931 var fg = e.findParent('.form-group', false, true);
24932 if (Roo.bootstrap.version == 3) {
24933 fg.removeClass([_this.invalidClass, _this.validClass]);
24934 fg.addClass(_this.invalidClass);
24936 fg.removeClass(['is-invalid', 'is-valid']);
24937 fg.addClass('is-invalid');
24945 var fg = this.el.findParent('.form-group', false, true);
24946 if (Roo.bootstrap.version == 3) {
24947 fg.removeClass([_this.invalidClass, _this.validClass]);
24948 fg.addClass(_this.invalidClass);
24950 fg.removeClass(['is-invalid', 'is-valid']);
24951 fg.addClass('is-invalid');
24956 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24962 for(var i in group){
24963 var fg = group[i].el.findParent('.form-group', false, true);
24964 if (Roo.bootstrap.version == 3) {
24965 fg.removeClass([_this.invalidClass, _this.validClass]);
24966 fg.addClass(_this.invalidClass);
24968 fg.removeClass(['is-invalid', 'is-valid']);
24969 fg.addClass('is-invalid');
24975 clearInvalid : function()
24977 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24979 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24981 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24983 if (label && label.iconEl) {
24984 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24985 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24989 disable : function()
24991 if(this.inputType != 'radio'){
24992 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24999 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25000 _this.getActionEl().addClass(this.disabledClass);
25001 e.dom.disabled = true;
25005 this.disabled = true;
25006 this.fireEvent("disable", this);
25010 enable : function()
25012 if(this.inputType != 'radio'){
25013 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25020 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25021 _this.getActionEl().removeClass(this.disabledClass);
25022 e.dom.disabled = false;
25026 this.disabled = false;
25027 this.fireEvent("enable", this);
25031 setBoxLabel : function(v)
25036 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25042 Roo.apply(Roo.bootstrap.CheckBox, {
25047 * register a CheckBox Group
25048 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25050 register : function(checkbox)
25052 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25053 this.groups[checkbox.groupId] = {};
25056 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25060 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25064 * fetch a CheckBox Group based on the group ID
25065 * @param {string} the group ID
25066 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25068 get: function(groupId) {
25069 if (typeof(this.groups[groupId]) == 'undefined') {
25073 return this.groups[groupId] ;
25086 * @class Roo.bootstrap.Radio
25087 * @extends Roo.bootstrap.Component
25088 * Bootstrap Radio class
25089 * @cfg {String} boxLabel - the label associated
25090 * @cfg {String} value - the value of radio
25093 * Create a new Radio
25094 * @param {Object} config The config object
25096 Roo.bootstrap.Radio = function(config){
25097 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25101 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25107 getAutoCreate : function()
25111 cls : 'form-group radio',
25116 html : this.boxLabel
25124 initEvents : function()
25126 this.parent().register(this);
25128 this.el.on('click', this.onClick, this);
25132 onClick : function(e)
25134 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25135 this.setChecked(true);
25139 setChecked : function(state, suppressEvent)
25141 this.parent().setValue(this.value, suppressEvent);
25145 setBoxLabel : function(v)
25150 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25165 * @class Roo.bootstrap.SecurePass
25166 * @extends Roo.bootstrap.Input
25167 * Bootstrap SecurePass class
25171 * Create a new SecurePass
25172 * @param {Object} config The config object
25175 Roo.bootstrap.SecurePass = function (config) {
25176 // these go here, so the translation tool can replace them..
25178 PwdEmpty: "Please type a password, and then retype it to confirm.",
25179 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25180 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25181 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25182 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25183 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25184 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25185 TooWeak: "Your password is Too Weak."
25187 this.meterLabel = "Password strength:";
25188 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25189 this.meterClass = [
25190 "roo-password-meter-tooweak",
25191 "roo-password-meter-weak",
25192 "roo-password-meter-medium",
25193 "roo-password-meter-strong",
25194 "roo-password-meter-grey"
25199 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25202 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25204 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25206 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25207 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25208 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25209 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25210 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25211 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25212 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25222 * @cfg {String/Object} Label for the strength meter (defaults to
25223 * 'Password strength:')
25228 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25229 * ['Weak', 'Medium', 'Strong'])
25232 pwdStrengths: false,
25245 initEvents: function ()
25247 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25249 if (this.el.is('input[type=password]') && Roo.isSafari) {
25250 this.el.on('keydown', this.SafariOnKeyDown, this);
25253 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25256 onRender: function (ct, position)
25258 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25259 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25260 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25262 this.trigger.createChild({
25267 cls: 'roo-password-meter-grey col-xs-12',
25270 //width: this.meterWidth + 'px'
25274 cls: 'roo-password-meter-text'
25280 if (this.hideTrigger) {
25281 this.trigger.setDisplayed(false);
25283 this.setSize(this.width || '', this.height || '');
25286 onDestroy: function ()
25288 if (this.trigger) {
25289 this.trigger.removeAllListeners();
25290 this.trigger.remove();
25293 this.wrap.remove();
25295 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25298 checkStrength: function ()
25300 var pwd = this.inputEl().getValue();
25301 if (pwd == this._lastPwd) {
25306 if (this.ClientSideStrongPassword(pwd)) {
25308 } else if (this.ClientSideMediumPassword(pwd)) {
25310 } else if (this.ClientSideWeakPassword(pwd)) {
25316 Roo.log('strength1: ' + strength);
25318 //var pm = this.trigger.child('div/div/div').dom;
25319 var pm = this.trigger.child('div/div');
25320 pm.removeClass(this.meterClass);
25321 pm.addClass(this.meterClass[strength]);
25324 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25326 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25328 this._lastPwd = pwd;
25332 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25334 this._lastPwd = '';
25336 var pm = this.trigger.child('div/div');
25337 pm.removeClass(this.meterClass);
25338 pm.addClass('roo-password-meter-grey');
25341 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25344 this.inputEl().dom.type='password';
25347 validateValue: function (value)
25349 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25352 if (value.length == 0) {
25353 if (this.allowBlank) {
25354 this.clearInvalid();
25358 this.markInvalid(this.errors.PwdEmpty);
25359 this.errorMsg = this.errors.PwdEmpty;
25367 if (!value.match(/[\x21-\x7e]+/)) {
25368 this.markInvalid(this.errors.PwdBadChar);
25369 this.errorMsg = this.errors.PwdBadChar;
25372 if (value.length < 6) {
25373 this.markInvalid(this.errors.PwdShort);
25374 this.errorMsg = this.errors.PwdShort;
25377 if (value.length > 16) {
25378 this.markInvalid(this.errors.PwdLong);
25379 this.errorMsg = this.errors.PwdLong;
25383 if (this.ClientSideStrongPassword(value)) {
25385 } else if (this.ClientSideMediumPassword(value)) {
25387 } else if (this.ClientSideWeakPassword(value)) {
25394 if (strength < 2) {
25395 //this.markInvalid(this.errors.TooWeak);
25396 this.errorMsg = this.errors.TooWeak;
25401 console.log('strength2: ' + strength);
25403 //var pm = this.trigger.child('div/div/div').dom;
25405 var pm = this.trigger.child('div/div');
25406 pm.removeClass(this.meterClass);
25407 pm.addClass(this.meterClass[strength]);
25409 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25411 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25413 this.errorMsg = '';
25417 CharacterSetChecks: function (type)
25420 this.fResult = false;
25423 isctype: function (character, type)
25426 case this.kCapitalLetter:
25427 if (character >= 'A' && character <= 'Z') {
25432 case this.kSmallLetter:
25433 if (character >= 'a' && character <= 'z') {
25439 if (character >= '0' && character <= '9') {
25444 case this.kPunctuation:
25445 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25456 IsLongEnough: function (pwd, size)
25458 return !(pwd == null || isNaN(size) || pwd.length < size);
25461 SpansEnoughCharacterSets: function (word, nb)
25463 if (!this.IsLongEnough(word, nb))
25468 var characterSetChecks = new Array(
25469 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25470 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25473 for (var index = 0; index < word.length; ++index) {
25474 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25475 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25476 characterSetChecks[nCharSet].fResult = true;
25483 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25484 if (characterSetChecks[nCharSet].fResult) {
25489 if (nCharSets < nb) {
25495 ClientSideStrongPassword: function (pwd)
25497 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25500 ClientSideMediumPassword: function (pwd)
25502 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25505 ClientSideWeakPassword: function (pwd)
25507 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25510 })//<script type="text/javascript">
25513 * Based Ext JS Library 1.1.1
25514 * Copyright(c) 2006-2007, Ext JS, LLC.
25520 * @class Roo.HtmlEditorCore
25521 * @extends Roo.Component
25522 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25524 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25527 Roo.HtmlEditorCore = function(config){
25530 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25535 * @event initialize
25536 * Fires when the editor is fully initialized (including the iframe)
25537 * @param {Roo.HtmlEditorCore} this
25542 * Fires when the editor is first receives the focus. Any insertion must wait
25543 * until after this event.
25544 * @param {Roo.HtmlEditorCore} this
25548 * @event beforesync
25549 * Fires before the textarea is updated with content from the editor iframe. Return false
25550 * to cancel the sync.
25551 * @param {Roo.HtmlEditorCore} this
25552 * @param {String} html
25556 * @event beforepush
25557 * Fires before the iframe editor is updated with content from the textarea. Return false
25558 * to cancel the push.
25559 * @param {Roo.HtmlEditorCore} this
25560 * @param {String} html
25565 * Fires when the textarea is updated with content from the editor iframe.
25566 * @param {Roo.HtmlEditorCore} this
25567 * @param {String} html
25572 * Fires when the iframe editor is updated with content from the textarea.
25573 * @param {Roo.HtmlEditorCore} this
25574 * @param {String} html
25579 * @event editorevent
25580 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25581 * @param {Roo.HtmlEditorCore} this
25587 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25589 // defaults : white / black...
25590 this.applyBlacklists();
25597 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25601 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25607 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25612 * @cfg {Number} height (in pixels)
25616 * @cfg {Number} width (in pixels)
25621 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25624 stylesheets: false,
25627 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25629 allowComments: false,
25633 // private properties
25634 validationEvent : false,
25636 initialized : false,
25638 sourceEditMode : false,
25639 onFocus : Roo.emptyFn,
25641 hideMode:'offsets',
25645 // blacklist + whitelisted elements..
25652 * Protected method that will not generally be called directly. It
25653 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25654 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25656 getDocMarkup : function(){
25660 // inherit styels from page...??
25661 if (this.stylesheets === false) {
25663 Roo.get(document.head).select('style').each(function(node) {
25664 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25667 Roo.get(document.head).select('link').each(function(node) {
25668 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25671 } else if (!this.stylesheets.length) {
25673 st = '<style type="text/css">' +
25674 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25677 for (var i in this.stylesheets) {
25678 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25683 st += '<style type="text/css">' +
25684 'IMG { cursor: pointer } ' +
25687 var cls = 'roo-htmleditor-body';
25689 if(this.bodyCls.length){
25690 cls += ' ' + this.bodyCls;
25693 return '<html><head>' + st +
25694 //<style type="text/css">' +
25695 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25697 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25701 onRender : function(ct, position)
25704 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25705 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25708 this.el.dom.style.border = '0 none';
25709 this.el.dom.setAttribute('tabIndex', -1);
25710 this.el.addClass('x-hidden hide');
25714 if(Roo.isIE){ // fix IE 1px bogus margin
25715 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25719 this.frameId = Roo.id();
25723 var iframe = this.owner.wrap.createChild({
25725 cls: 'form-control', // bootstrap..
25727 name: this.frameId,
25728 frameBorder : 'no',
25729 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25734 this.iframe = iframe.dom;
25736 this.assignDocWin();
25738 this.doc.designMode = 'on';
25741 this.doc.write(this.getDocMarkup());
25745 var task = { // must defer to wait for browser to be ready
25747 //console.log("run task?" + this.doc.readyState);
25748 this.assignDocWin();
25749 if(this.doc.body || this.doc.readyState == 'complete'){
25751 this.doc.designMode="on";
25755 Roo.TaskMgr.stop(task);
25756 this.initEditor.defer(10, this);
25763 Roo.TaskMgr.start(task);
25768 onResize : function(w, h)
25770 Roo.log('resize: ' +w + ',' + h );
25771 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25775 if(typeof w == 'number'){
25777 this.iframe.style.width = w + 'px';
25779 if(typeof h == 'number'){
25781 this.iframe.style.height = h + 'px';
25783 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25790 * Toggles the editor between standard and source edit mode.
25791 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25793 toggleSourceEdit : function(sourceEditMode){
25795 this.sourceEditMode = sourceEditMode === true;
25797 if(this.sourceEditMode){
25799 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25802 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25803 //this.iframe.className = '';
25806 //this.setSize(this.owner.wrap.getSize());
25807 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25814 * Protected method that will not generally be called directly. If you need/want
25815 * custom HTML cleanup, this is the method you should override.
25816 * @param {String} html The HTML to be cleaned
25817 * return {String} The cleaned HTML
25819 cleanHtml : function(html){
25820 html = String(html);
25821 if(html.length > 5){
25822 if(Roo.isSafari){ // strip safari nonsense
25823 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25826 if(html == ' '){
25833 * HTML Editor -> Textarea
25834 * Protected method that will not generally be called directly. Syncs the contents
25835 * of the editor iframe with the textarea.
25837 syncValue : function(){
25838 if(this.initialized){
25839 var bd = (this.doc.body || this.doc.documentElement);
25840 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25841 var html = bd.innerHTML;
25843 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25844 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25846 html = '<div style="'+m[0]+'">' + html + '</div>';
25849 html = this.cleanHtml(html);
25850 // fix up the special chars.. normaly like back quotes in word...
25851 // however we do not want to do this with chinese..
25852 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25854 var cc = match.charCodeAt();
25856 // Get the character value, handling surrogate pairs
25857 if (match.length == 2) {
25858 // It's a surrogate pair, calculate the Unicode code point
25859 var high = match.charCodeAt(0) - 0xD800;
25860 var low = match.charCodeAt(1) - 0xDC00;
25861 cc = (high * 0x400) + low + 0x10000;
25863 (cc >= 0x4E00 && cc < 0xA000 ) ||
25864 (cc >= 0x3400 && cc < 0x4E00 ) ||
25865 (cc >= 0xf900 && cc < 0xfb00 )
25870 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25871 return "&#" + cc + ";";
25878 if(this.owner.fireEvent('beforesync', this, html) !== false){
25879 this.el.dom.value = html;
25880 this.owner.fireEvent('sync', this, html);
25886 * Protected method that will not generally be called directly. Pushes the value of the textarea
25887 * into the iframe editor.
25889 pushValue : function(){
25890 if(this.initialized){
25891 var v = this.el.dom.value.trim();
25893 // if(v.length < 1){
25897 if(this.owner.fireEvent('beforepush', this, v) !== false){
25898 var d = (this.doc.body || this.doc.documentElement);
25900 this.cleanUpPaste();
25901 this.el.dom.value = d.innerHTML;
25902 this.owner.fireEvent('push', this, v);
25908 deferFocus : function(){
25909 this.focus.defer(10, this);
25913 focus : function(){
25914 if(this.win && !this.sourceEditMode){
25921 assignDocWin: function()
25923 var iframe = this.iframe;
25926 this.doc = iframe.contentWindow.document;
25927 this.win = iframe.contentWindow;
25929 // if (!Roo.get(this.frameId)) {
25932 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25933 // this.win = Roo.get(this.frameId).dom.contentWindow;
25935 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25939 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25940 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25945 initEditor : function(){
25946 //console.log("INIT EDITOR");
25947 this.assignDocWin();
25951 this.doc.designMode="on";
25953 this.doc.write(this.getDocMarkup());
25956 var dbody = (this.doc.body || this.doc.documentElement);
25957 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25958 // this copies styles from the containing element into thsi one..
25959 // not sure why we need all of this..
25960 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25962 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25963 //ss['background-attachment'] = 'fixed'; // w3c
25964 dbody.bgProperties = 'fixed'; // ie
25965 //Roo.DomHelper.applyStyles(dbody, ss);
25966 Roo.EventManager.on(this.doc, {
25967 //'mousedown': this.onEditorEvent,
25968 'mouseup': this.onEditorEvent,
25969 'dblclick': this.onEditorEvent,
25970 'click': this.onEditorEvent,
25971 'keyup': this.onEditorEvent,
25976 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25978 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25979 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25981 this.initialized = true;
25983 this.owner.fireEvent('initialize', this);
25988 onDestroy : function(){
25994 //for (var i =0; i < this.toolbars.length;i++) {
25995 // // fixme - ask toolbars for heights?
25996 // this.toolbars[i].onDestroy();
25999 //this.wrap.dom.innerHTML = '';
26000 //this.wrap.remove();
26005 onFirstFocus : function(){
26007 this.assignDocWin();
26010 this.activated = true;
26013 if(Roo.isGecko){ // prevent silly gecko errors
26015 var s = this.win.getSelection();
26016 if(!s.focusNode || s.focusNode.nodeType != 3){
26017 var r = s.getRangeAt(0);
26018 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26023 this.execCmd('useCSS', true);
26024 this.execCmd('styleWithCSS', false);
26027 this.owner.fireEvent('activate', this);
26031 adjustFont: function(btn){
26032 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26033 //if(Roo.isSafari){ // safari
26036 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26037 if(Roo.isSafari){ // safari
26038 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26039 v = (v < 10) ? 10 : v;
26040 v = (v > 48) ? 48 : v;
26041 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26046 v = Math.max(1, v+adjust);
26048 this.execCmd('FontSize', v );
26051 onEditorEvent : function(e)
26053 this.owner.fireEvent('editorevent', this, e);
26054 // this.updateToolbar();
26055 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26058 insertTag : function(tg)
26060 // could be a bit smarter... -> wrap the current selected tRoo..
26061 if (tg.toLowerCase() == 'span' ||
26062 tg.toLowerCase() == 'code' ||
26063 tg.toLowerCase() == 'sup' ||
26064 tg.toLowerCase() == 'sub'
26067 range = this.createRange(this.getSelection());
26068 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26069 wrappingNode.appendChild(range.extractContents());
26070 range.insertNode(wrappingNode);
26077 this.execCmd("formatblock", tg);
26081 insertText : function(txt)
26085 var range = this.createRange();
26086 range.deleteContents();
26087 //alert(Sender.getAttribute('label'));
26089 range.insertNode(this.doc.createTextNode(txt));
26095 * Executes a Midas editor command on the editor document and performs necessary focus and
26096 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26097 * @param {String} cmd The Midas command
26098 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26100 relayCmd : function(cmd, value){
26102 this.execCmd(cmd, value);
26103 this.owner.fireEvent('editorevent', this);
26104 //this.updateToolbar();
26105 this.owner.deferFocus();
26109 * Executes a Midas editor command directly on the editor document.
26110 * For visual commands, you should use {@link #relayCmd} instead.
26111 * <b>This should only be called after the editor is initialized.</b>
26112 * @param {String} cmd The Midas command
26113 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26115 execCmd : function(cmd, value){
26116 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26123 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26125 * @param {String} text | dom node..
26127 insertAtCursor : function(text)
26130 if(!this.activated){
26136 var r = this.doc.selection.createRange();
26147 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26151 // from jquery ui (MIT licenced)
26153 var win = this.win;
26155 if (win.getSelection && win.getSelection().getRangeAt) {
26156 range = win.getSelection().getRangeAt(0);
26157 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26158 range.insertNode(node);
26159 } else if (win.document.selection && win.document.selection.createRange) {
26160 // no firefox support
26161 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26162 win.document.selection.createRange().pasteHTML(txt);
26164 // no firefox support
26165 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26166 this.execCmd('InsertHTML', txt);
26175 mozKeyPress : function(e){
26177 var c = e.getCharCode(), cmd;
26180 c = String.fromCharCode(c).toLowerCase();
26194 this.cleanUpPaste.defer(100, this);
26202 e.preventDefault();
26210 fixKeys : function(){ // load time branching for fastest keydown performance
26212 return function(e){
26213 var k = e.getKey(), r;
26216 r = this.doc.selection.createRange();
26219 r.pasteHTML('    ');
26226 r = this.doc.selection.createRange();
26228 var target = r.parentElement();
26229 if(!target || target.tagName.toLowerCase() != 'li'){
26231 r.pasteHTML('<br />');
26237 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26238 this.cleanUpPaste.defer(100, this);
26244 }else if(Roo.isOpera){
26245 return function(e){
26246 var k = e.getKey();
26250 this.execCmd('InsertHTML','    ');
26253 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26254 this.cleanUpPaste.defer(100, this);
26259 }else if(Roo.isSafari){
26260 return function(e){
26261 var k = e.getKey();
26265 this.execCmd('InsertText','\t');
26269 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26270 this.cleanUpPaste.defer(100, this);
26278 getAllAncestors: function()
26280 var p = this.getSelectedNode();
26283 a.push(p); // push blank onto stack..
26284 p = this.getParentElement();
26288 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26292 a.push(this.doc.body);
26296 lastSelNode : false,
26299 getSelection : function()
26301 this.assignDocWin();
26302 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26305 getSelectedNode: function()
26307 // this may only work on Gecko!!!
26309 // should we cache this!!!!
26314 var range = this.createRange(this.getSelection()).cloneRange();
26317 var parent = range.parentElement();
26319 var testRange = range.duplicate();
26320 testRange.moveToElementText(parent);
26321 if (testRange.inRange(range)) {
26324 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26327 parent = parent.parentElement;
26332 // is ancestor a text element.
26333 var ac = range.commonAncestorContainer;
26334 if (ac.nodeType == 3) {
26335 ac = ac.parentNode;
26338 var ar = ac.childNodes;
26341 var other_nodes = [];
26342 var has_other_nodes = false;
26343 for (var i=0;i<ar.length;i++) {
26344 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26347 // fullly contained node.
26349 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26354 // probably selected..
26355 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26356 other_nodes.push(ar[i]);
26360 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26365 has_other_nodes = true;
26367 if (!nodes.length && other_nodes.length) {
26368 nodes= other_nodes;
26370 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26376 createRange: function(sel)
26378 // this has strange effects when using with
26379 // top toolbar - not sure if it's a great idea.
26380 //this.editor.contentWindow.focus();
26381 if (typeof sel != "undefined") {
26383 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26385 return this.doc.createRange();
26388 return this.doc.createRange();
26391 getParentElement: function()
26394 this.assignDocWin();
26395 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26397 var range = this.createRange(sel);
26400 var p = range.commonAncestorContainer;
26401 while (p.nodeType == 3) { // text node
26412 * Range intersection.. the hard stuff...
26416 * [ -- selected range --- ]
26420 * if end is before start or hits it. fail.
26421 * if start is after end or hits it fail.
26423 * if either hits (but other is outside. - then it's not
26429 // @see http://www.thismuchiknow.co.uk/?p=64.
26430 rangeIntersectsNode : function(range, node)
26432 var nodeRange = node.ownerDocument.createRange();
26434 nodeRange.selectNode(node);
26436 nodeRange.selectNodeContents(node);
26439 var rangeStartRange = range.cloneRange();
26440 rangeStartRange.collapse(true);
26442 var rangeEndRange = range.cloneRange();
26443 rangeEndRange.collapse(false);
26445 var nodeStartRange = nodeRange.cloneRange();
26446 nodeStartRange.collapse(true);
26448 var nodeEndRange = nodeRange.cloneRange();
26449 nodeEndRange.collapse(false);
26451 return rangeStartRange.compareBoundaryPoints(
26452 Range.START_TO_START, nodeEndRange) == -1 &&
26453 rangeEndRange.compareBoundaryPoints(
26454 Range.START_TO_START, nodeStartRange) == 1;
26458 rangeCompareNode : function(range, node)
26460 var nodeRange = node.ownerDocument.createRange();
26462 nodeRange.selectNode(node);
26464 nodeRange.selectNodeContents(node);
26468 range.collapse(true);
26470 nodeRange.collapse(true);
26472 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26473 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26475 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26477 var nodeIsBefore = ss == 1;
26478 var nodeIsAfter = ee == -1;
26480 if (nodeIsBefore && nodeIsAfter) {
26483 if (!nodeIsBefore && nodeIsAfter) {
26484 return 1; //right trailed.
26487 if (nodeIsBefore && !nodeIsAfter) {
26488 return 2; // left trailed.
26494 // private? - in a new class?
26495 cleanUpPaste : function()
26497 // cleans up the whole document..
26498 Roo.log('cleanuppaste');
26500 this.cleanUpChildren(this.doc.body);
26501 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26502 if (clean != this.doc.body.innerHTML) {
26503 this.doc.body.innerHTML = clean;
26508 cleanWordChars : function(input) {// change the chars to hex code
26509 var he = Roo.HtmlEditorCore;
26511 var output = input;
26512 Roo.each(he.swapCodes, function(sw) {
26513 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26515 output = output.replace(swapper, sw[1]);
26522 cleanUpChildren : function (n)
26524 if (!n.childNodes.length) {
26527 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26528 this.cleanUpChild(n.childNodes[i]);
26535 cleanUpChild : function (node)
26538 //console.log(node);
26539 if (node.nodeName == "#text") {
26540 // clean up silly Windows -- stuff?
26543 if (node.nodeName == "#comment") {
26544 if (!this.allowComments) {
26545 node.parentNode.removeChild(node);
26547 // clean up silly Windows -- stuff?
26550 var lcname = node.tagName.toLowerCase();
26551 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26552 // whitelist of tags..
26554 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26556 node.parentNode.removeChild(node);
26561 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26563 // spans with no attributes - just remove them..
26564 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26565 remove_keep_children = true;
26568 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26569 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26571 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26572 // remove_keep_children = true;
26575 if (remove_keep_children) {
26576 this.cleanUpChildren(node);
26577 // inserts everything just before this node...
26578 while (node.childNodes.length) {
26579 var cn = node.childNodes[0];
26580 node.removeChild(cn);
26581 node.parentNode.insertBefore(cn, node);
26583 node.parentNode.removeChild(node);
26587 if (!node.attributes || !node.attributes.length) {
26592 this.cleanUpChildren(node);
26596 function cleanAttr(n,v)
26599 if (v.match(/^\./) || v.match(/^\//)) {
26602 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26605 if (v.match(/^#/)) {
26608 if (v.match(/^\{/)) { // allow template editing.
26611 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26612 node.removeAttribute(n);
26616 var cwhite = this.cwhite;
26617 var cblack = this.cblack;
26619 function cleanStyle(n,v)
26621 if (v.match(/expression/)) { //XSS?? should we even bother..
26622 node.removeAttribute(n);
26626 var parts = v.split(/;/);
26629 Roo.each(parts, function(p) {
26630 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26634 var l = p.split(':').shift().replace(/\s+/g,'');
26635 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26637 if ( cwhite.length && cblack.indexOf(l) > -1) {
26638 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26639 //node.removeAttribute(n);
26643 // only allow 'c whitelisted system attributes'
26644 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26645 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26646 //node.removeAttribute(n);
26656 if (clean.length) {
26657 node.setAttribute(n, clean.join(';'));
26659 node.removeAttribute(n);
26665 for (var i = node.attributes.length-1; i > -1 ; i--) {
26666 var a = node.attributes[i];
26669 if (a.name.toLowerCase().substr(0,2)=='on') {
26670 node.removeAttribute(a.name);
26673 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26674 node.removeAttribute(a.name);
26677 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26678 cleanAttr(a.name,a.value); // fixme..
26681 if (a.name == 'style') {
26682 cleanStyle(a.name,a.value);
26685 /// clean up MS crap..
26686 // tecnically this should be a list of valid class'es..
26689 if (a.name == 'class') {
26690 if (a.value.match(/^Mso/)) {
26691 node.removeAttribute('class');
26694 if (a.value.match(/^body$/)) {
26695 node.removeAttribute('class');
26706 this.cleanUpChildren(node);
26712 * Clean up MS wordisms...
26714 cleanWord : function(node)
26717 this.cleanWord(this.doc.body);
26722 node.nodeName == 'SPAN' &&
26723 !node.hasAttributes() &&
26724 node.childNodes.length == 1 &&
26725 node.firstChild.nodeName == "#text"
26727 var textNode = node.firstChild;
26728 node.removeChild(textNode);
26729 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26730 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26732 node.parentNode.insertBefore(textNode, node);
26733 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26734 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26736 node.parentNode.removeChild(node);
26739 if (node.nodeName == "#text") {
26740 // clean up silly Windows -- stuff?
26743 if (node.nodeName == "#comment") {
26744 node.parentNode.removeChild(node);
26745 // clean up silly Windows -- stuff?
26749 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26750 node.parentNode.removeChild(node);
26753 //Roo.log(node.tagName);
26754 // remove - but keep children..
26755 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26756 //Roo.log('-- removed');
26757 while (node.childNodes.length) {
26758 var cn = node.childNodes[0];
26759 node.removeChild(cn);
26760 node.parentNode.insertBefore(cn, node);
26761 // move node to parent - and clean it..
26762 this.cleanWord(cn);
26764 node.parentNode.removeChild(node);
26765 /// no need to iterate chidlren = it's got none..
26766 //this.iterateChildren(node, this.cleanWord);
26770 if (node.className.length) {
26772 var cn = node.className.split(/\W+/);
26774 Roo.each(cn, function(cls) {
26775 if (cls.match(/Mso[a-zA-Z]+/)) {
26780 node.className = cna.length ? cna.join(' ') : '';
26782 node.removeAttribute("class");
26786 if (node.hasAttribute("lang")) {
26787 node.removeAttribute("lang");
26790 if (node.hasAttribute("style")) {
26792 var styles = node.getAttribute("style").split(";");
26794 Roo.each(styles, function(s) {
26795 if (!s.match(/:/)) {
26798 var kv = s.split(":");
26799 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26802 // what ever is left... we allow.
26805 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26806 if (!nstyle.length) {
26807 node.removeAttribute('style');
26810 this.iterateChildren(node, this.cleanWord);
26816 * iterateChildren of a Node, calling fn each time, using this as the scole..
26817 * @param {DomNode} node node to iterate children of.
26818 * @param {Function} fn method of this class to call on each item.
26820 iterateChildren : function(node, fn)
26822 if (!node.childNodes.length) {
26825 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26826 fn.call(this, node.childNodes[i])
26832 * cleanTableWidths.
26834 * Quite often pasting from word etc.. results in tables with column and widths.
26835 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26838 cleanTableWidths : function(node)
26843 this.cleanTableWidths(this.doc.body);
26848 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26851 Roo.log(node.tagName);
26852 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26853 this.iterateChildren(node, this.cleanTableWidths);
26856 if (node.hasAttribute('width')) {
26857 node.removeAttribute('width');
26861 if (node.hasAttribute("style")) {
26864 var styles = node.getAttribute("style").split(";");
26866 Roo.each(styles, function(s) {
26867 if (!s.match(/:/)) {
26870 var kv = s.split(":");
26871 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26874 // what ever is left... we allow.
26877 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26878 if (!nstyle.length) {
26879 node.removeAttribute('style');
26883 this.iterateChildren(node, this.cleanTableWidths);
26891 domToHTML : function(currentElement, depth, nopadtext) {
26893 depth = depth || 0;
26894 nopadtext = nopadtext || false;
26896 if (!currentElement) {
26897 return this.domToHTML(this.doc.body);
26900 //Roo.log(currentElement);
26902 var allText = false;
26903 var nodeName = currentElement.nodeName;
26904 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26906 if (nodeName == '#text') {
26908 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26913 if (nodeName != 'BODY') {
26916 // Prints the node tagName, such as <A>, <IMG>, etc
26919 for(i = 0; i < currentElement.attributes.length;i++) {
26921 var aname = currentElement.attributes.item(i).name;
26922 if (!currentElement.attributes.item(i).value.length) {
26925 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26928 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26937 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26940 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26945 // Traverse the tree
26947 var currentElementChild = currentElement.childNodes.item(i);
26948 var allText = true;
26949 var innerHTML = '';
26951 while (currentElementChild) {
26952 // Formatting code (indent the tree so it looks nice on the screen)
26953 var nopad = nopadtext;
26954 if (lastnode == 'SPAN') {
26958 if (currentElementChild.nodeName == '#text') {
26959 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26960 toadd = nopadtext ? toadd : toadd.trim();
26961 if (!nopad && toadd.length > 80) {
26962 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26964 innerHTML += toadd;
26967 currentElementChild = currentElement.childNodes.item(i);
26973 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26975 // Recursively traverse the tree structure of the child node
26976 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26977 lastnode = currentElementChild.nodeName;
26979 currentElementChild=currentElement.childNodes.item(i);
26985 // The remaining code is mostly for formatting the tree
26986 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26991 ret+= "</"+tagName+">";
26997 applyBlacklists : function()
26999 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27000 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27004 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27005 if (b.indexOf(tag) > -1) {
27008 this.white.push(tag);
27012 Roo.each(w, function(tag) {
27013 if (b.indexOf(tag) > -1) {
27016 if (this.white.indexOf(tag) > -1) {
27019 this.white.push(tag);
27024 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27025 if (w.indexOf(tag) > -1) {
27028 this.black.push(tag);
27032 Roo.each(b, function(tag) {
27033 if (w.indexOf(tag) > -1) {
27036 if (this.black.indexOf(tag) > -1) {
27039 this.black.push(tag);
27044 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27045 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27049 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27050 if (b.indexOf(tag) > -1) {
27053 this.cwhite.push(tag);
27057 Roo.each(w, function(tag) {
27058 if (b.indexOf(tag) > -1) {
27061 if (this.cwhite.indexOf(tag) > -1) {
27064 this.cwhite.push(tag);
27069 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27070 if (w.indexOf(tag) > -1) {
27073 this.cblack.push(tag);
27077 Roo.each(b, function(tag) {
27078 if (w.indexOf(tag) > -1) {
27081 if (this.cblack.indexOf(tag) > -1) {
27084 this.cblack.push(tag);
27089 setStylesheets : function(stylesheets)
27091 if(typeof(stylesheets) == 'string'){
27092 Roo.get(this.iframe.contentDocument.head).createChild({
27094 rel : 'stylesheet',
27103 Roo.each(stylesheets, function(s) {
27108 Roo.get(_this.iframe.contentDocument.head).createChild({
27110 rel : 'stylesheet',
27119 removeStylesheets : function()
27123 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27128 setStyle : function(style)
27130 Roo.get(this.iframe.contentDocument.head).createChild({
27139 // hide stuff that is not compatible
27153 * @event specialkey
27157 * @cfg {String} fieldClass @hide
27160 * @cfg {String} focusClass @hide
27163 * @cfg {String} autoCreate @hide
27166 * @cfg {String} inputType @hide
27169 * @cfg {String} invalidClass @hide
27172 * @cfg {String} invalidText @hide
27175 * @cfg {String} msgFx @hide
27178 * @cfg {String} validateOnBlur @hide
27182 Roo.HtmlEditorCore.white = [
27183 'area', 'br', 'img', 'input', 'hr', 'wbr',
27185 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27186 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27187 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27188 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27189 'table', 'ul', 'xmp',
27191 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27194 'dir', 'menu', 'ol', 'ul', 'dl',
27200 Roo.HtmlEditorCore.black = [
27201 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27203 'base', 'basefont', 'bgsound', 'blink', 'body',
27204 'frame', 'frameset', 'head', 'html', 'ilayer',
27205 'iframe', 'layer', 'link', 'meta', 'object',
27206 'script', 'style' ,'title', 'xml' // clean later..
27208 Roo.HtmlEditorCore.clean = [
27209 'script', 'style', 'title', 'xml'
27211 Roo.HtmlEditorCore.remove = [
27216 Roo.HtmlEditorCore.ablack = [
27220 Roo.HtmlEditorCore.aclean = [
27221 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27225 Roo.HtmlEditorCore.pwhite= [
27226 'http', 'https', 'mailto'
27229 // white listed style attributes.
27230 Roo.HtmlEditorCore.cwhite= [
27231 // 'text-align', /// default is to allow most things..
27237 // black listed style attributes.
27238 Roo.HtmlEditorCore.cblack= [
27239 // 'font-size' -- this can be set by the project
27243 Roo.HtmlEditorCore.swapCodes =[
27244 [ 8211, "–" ],
27245 [ 8212, "—" ],
27262 * @class Roo.bootstrap.HtmlEditor
27263 * @extends Roo.bootstrap.TextArea
27264 * Bootstrap HtmlEditor class
27267 * Create a new HtmlEditor
27268 * @param {Object} config The config object
27271 Roo.bootstrap.HtmlEditor = function(config){
27272 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27273 if (!this.toolbars) {
27274 this.toolbars = [];
27277 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27280 * @event initialize
27281 * Fires when the editor is fully initialized (including the iframe)
27282 * @param {HtmlEditor} this
27287 * Fires when the editor is first receives the focus. Any insertion must wait
27288 * until after this event.
27289 * @param {HtmlEditor} this
27293 * @event beforesync
27294 * Fires before the textarea is updated with content from the editor iframe. Return false
27295 * to cancel the sync.
27296 * @param {HtmlEditor} this
27297 * @param {String} html
27301 * @event beforepush
27302 * Fires before the iframe editor is updated with content from the textarea. Return false
27303 * to cancel the push.
27304 * @param {HtmlEditor} this
27305 * @param {String} html
27310 * Fires when the textarea is updated with content from the editor iframe.
27311 * @param {HtmlEditor} this
27312 * @param {String} html
27317 * Fires when the iframe editor is updated with content from the textarea.
27318 * @param {HtmlEditor} this
27319 * @param {String} html
27323 * @event editmodechange
27324 * Fires when the editor switches edit modes
27325 * @param {HtmlEditor} this
27326 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27328 editmodechange: true,
27330 * @event editorevent
27331 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27332 * @param {HtmlEditor} this
27336 * @event firstfocus
27337 * Fires when on first focus - needed by toolbars..
27338 * @param {HtmlEditor} this
27343 * Auto save the htmlEditor value as a file into Events
27344 * @param {HtmlEditor} this
27348 * @event savedpreview
27349 * preview the saved version of htmlEditor
27350 * @param {HtmlEditor} this
27357 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27361 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27366 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27371 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27376 * @cfg {Number} height (in pixels)
27380 * @cfg {Number} width (in pixels)
27385 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27388 stylesheets: false,
27393 // private properties
27394 validationEvent : false,
27396 initialized : false,
27399 onFocus : Roo.emptyFn,
27401 hideMode:'offsets',
27403 tbContainer : false,
27407 toolbarContainer :function() {
27408 return this.wrap.select('.x-html-editor-tb',true).first();
27412 * Protected method that will not generally be called directly. It
27413 * is called when the editor creates its toolbar. Override this method if you need to
27414 * add custom toolbar buttons.
27415 * @param {HtmlEditor} editor
27417 createToolbar : function(){
27418 Roo.log('renewing');
27419 Roo.log("create toolbars");
27421 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27422 this.toolbars[0].render(this.toolbarContainer());
27426 // if (!editor.toolbars || !editor.toolbars.length) {
27427 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27430 // for (var i =0 ; i < editor.toolbars.length;i++) {
27431 // editor.toolbars[i] = Roo.factory(
27432 // typeof(editor.toolbars[i]) == 'string' ?
27433 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27434 // Roo.bootstrap.HtmlEditor);
27435 // editor.toolbars[i].init(editor);
27441 onRender : function(ct, position)
27443 // Roo.log("Call onRender: " + this.xtype);
27445 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27447 this.wrap = this.inputEl().wrap({
27448 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27451 this.editorcore.onRender(ct, position);
27453 if (this.resizable) {
27454 this.resizeEl = new Roo.Resizable(this.wrap, {
27458 minHeight : this.height,
27459 height: this.height,
27460 handles : this.resizable,
27463 resize : function(r, w, h) {
27464 _t.onResize(w,h); // -something
27470 this.createToolbar(this);
27473 if(!this.width && this.resizable){
27474 this.setSize(this.wrap.getSize());
27476 if (this.resizeEl) {
27477 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27478 // should trigger onReize..
27484 onResize : function(w, h)
27486 Roo.log('resize: ' +w + ',' + h );
27487 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27491 if(this.inputEl() ){
27492 if(typeof w == 'number'){
27493 var aw = w - this.wrap.getFrameWidth('lr');
27494 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27497 if(typeof h == 'number'){
27498 var tbh = -11; // fixme it needs to tool bar size!
27499 for (var i =0; i < this.toolbars.length;i++) {
27500 // fixme - ask toolbars for heights?
27501 tbh += this.toolbars[i].el.getHeight();
27502 //if (this.toolbars[i].footer) {
27503 // tbh += this.toolbars[i].footer.el.getHeight();
27511 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27512 ah -= 5; // knock a few pixes off for look..
27513 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27517 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27518 this.editorcore.onResize(ew,eh);
27523 * Toggles the editor between standard and source edit mode.
27524 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27526 toggleSourceEdit : function(sourceEditMode)
27528 this.editorcore.toggleSourceEdit(sourceEditMode);
27530 if(this.editorcore.sourceEditMode){
27531 Roo.log('editor - showing textarea');
27534 // Roo.log(this.syncValue());
27536 this.inputEl().removeClass(['hide', 'x-hidden']);
27537 this.inputEl().dom.removeAttribute('tabIndex');
27538 this.inputEl().focus();
27540 Roo.log('editor - hiding textarea');
27542 // Roo.log(this.pushValue());
27545 this.inputEl().addClass(['hide', 'x-hidden']);
27546 this.inputEl().dom.setAttribute('tabIndex', -1);
27547 //this.deferFocus();
27550 if(this.resizable){
27551 this.setSize(this.wrap.getSize());
27554 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27557 // private (for BoxComponent)
27558 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27560 // private (for BoxComponent)
27561 getResizeEl : function(){
27565 // private (for BoxComponent)
27566 getPositionEl : function(){
27571 initEvents : function(){
27572 this.originalValue = this.getValue();
27576 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27579 // markInvalid : Roo.emptyFn,
27581 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27584 // clearInvalid : Roo.emptyFn,
27586 setValue : function(v){
27587 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27588 this.editorcore.pushValue();
27593 deferFocus : function(){
27594 this.focus.defer(10, this);
27598 focus : function(){
27599 this.editorcore.focus();
27605 onDestroy : function(){
27611 for (var i =0; i < this.toolbars.length;i++) {
27612 // fixme - ask toolbars for heights?
27613 this.toolbars[i].onDestroy();
27616 this.wrap.dom.innerHTML = '';
27617 this.wrap.remove();
27622 onFirstFocus : function(){
27623 //Roo.log("onFirstFocus");
27624 this.editorcore.onFirstFocus();
27625 for (var i =0; i < this.toolbars.length;i++) {
27626 this.toolbars[i].onFirstFocus();
27632 syncValue : function()
27634 this.editorcore.syncValue();
27637 pushValue : function()
27639 this.editorcore.pushValue();
27643 // hide stuff that is not compatible
27657 * @event specialkey
27661 * @cfg {String} fieldClass @hide
27664 * @cfg {String} focusClass @hide
27667 * @cfg {String} autoCreate @hide
27670 * @cfg {String} inputType @hide
27674 * @cfg {String} invalidText @hide
27677 * @cfg {String} msgFx @hide
27680 * @cfg {String} validateOnBlur @hide
27689 Roo.namespace('Roo.bootstrap.htmleditor');
27691 * @class Roo.bootstrap.HtmlEditorToolbar1
27697 new Roo.bootstrap.HtmlEditor({
27700 new Roo.bootstrap.HtmlEditorToolbar1({
27701 disable : { fonts: 1 , format: 1, ..., ... , ...],
27707 * @cfg {Object} disable List of elements to disable..
27708 * @cfg {Array} btns List of additional buttons.
27712 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27715 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27718 Roo.apply(this, config);
27720 // default disabled, based on 'good practice'..
27721 this.disable = this.disable || {};
27722 Roo.applyIf(this.disable, {
27725 specialElements : true
27727 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27729 this.editor = config.editor;
27730 this.editorcore = config.editor.editorcore;
27732 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27734 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27735 // dont call parent... till later.
27737 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27742 editorcore : false,
27747 "h1","h2","h3","h4","h5","h6",
27749 "abbr", "acronym", "address", "cite", "samp", "var",
27753 onRender : function(ct, position)
27755 // Roo.log("Call onRender: " + this.xtype);
27757 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27759 this.el.dom.style.marginBottom = '0';
27761 var editorcore = this.editorcore;
27762 var editor= this.editor;
27765 var btn = function(id,cmd , toggle, handler, html){
27767 var event = toggle ? 'toggle' : 'click';
27772 xns: Roo.bootstrap,
27776 enableToggle:toggle !== false,
27778 pressed : toggle ? false : null,
27781 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27782 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27788 // var cb_box = function...
27793 xns: Roo.bootstrap,
27798 xns: Roo.bootstrap,
27802 Roo.each(this.formats, function(f) {
27803 style.menu.items.push({
27805 xns: Roo.bootstrap,
27806 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27811 editorcore.insertTag(this.tagname);
27818 children.push(style);
27820 btn('bold',false,true);
27821 btn('italic',false,true);
27822 btn('align-left', 'justifyleft',true);
27823 btn('align-center', 'justifycenter',true);
27824 btn('align-right' , 'justifyright',true);
27825 btn('link', false, false, function(btn) {
27826 //Roo.log("create link?");
27827 var url = prompt(this.createLinkText, this.defaultLinkValue);
27828 if(url && url != 'http:/'+'/'){
27829 this.editorcore.relayCmd('createlink', url);
27832 btn('list','insertunorderedlist',true);
27833 btn('pencil', false,true, function(btn){
27835 this.toggleSourceEdit(btn.pressed);
27838 if (this.editor.btns.length > 0) {
27839 for (var i = 0; i<this.editor.btns.length; i++) {
27840 children.push(this.editor.btns[i]);
27848 xns: Roo.bootstrap,
27853 xns: Roo.bootstrap,
27858 cog.menu.items.push({
27860 xns: Roo.bootstrap,
27861 html : Clean styles,
27866 editorcore.insertTag(this.tagname);
27875 this.xtype = 'NavSimplebar';
27877 for(var i=0;i< children.length;i++) {
27879 this.buttons.add(this.addxtypeChild(children[i]));
27883 editor.on('editorevent', this.updateToolbar, this);
27885 onBtnClick : function(id)
27887 this.editorcore.relayCmd(id);
27888 this.editorcore.focus();
27892 * Protected method that will not generally be called directly. It triggers
27893 * a toolbar update by reading the markup state of the current selection in the editor.
27895 updateToolbar: function(){
27897 if(!this.editorcore.activated){
27898 this.editor.onFirstFocus(); // is this neeed?
27902 var btns = this.buttons;
27903 var doc = this.editorcore.doc;
27904 btns.get('bold').setActive(doc.queryCommandState('bold'));
27905 btns.get('italic').setActive(doc.queryCommandState('italic'));
27906 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27908 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27909 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27910 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27912 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27913 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27916 var ans = this.editorcore.getAllAncestors();
27917 if (this.formatCombo) {
27920 var store = this.formatCombo.store;
27921 this.formatCombo.setValue("");
27922 for (var i =0; i < ans.length;i++) {
27923 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27925 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27933 // hides menus... - so this cant be on a menu...
27934 Roo.bootstrap.MenuMgr.hideAll();
27936 Roo.bootstrap.MenuMgr.hideAll();
27937 //this.editorsyncValue();
27939 onFirstFocus: function() {
27940 this.buttons.each(function(item){
27944 toggleSourceEdit : function(sourceEditMode){
27947 if(sourceEditMode){
27948 Roo.log("disabling buttons");
27949 this.buttons.each( function(item){
27950 if(item.cmd != 'pencil'){
27956 Roo.log("enabling buttons");
27957 if(this.editorcore.initialized){
27958 this.buttons.each( function(item){
27964 Roo.log("calling toggole on editor");
27965 // tell the editor that it's been pressed..
27966 this.editor.toggleSourceEdit(sourceEditMode);
27980 * @class Roo.bootstrap.Markdown
27981 * @extends Roo.bootstrap.TextArea
27982 * Bootstrap Showdown editable area
27983 * @cfg {string} content
27986 * Create a new Showdown
27989 Roo.bootstrap.Markdown = function(config){
27990 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27994 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27998 initEvents : function()
28001 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28002 this.markdownEl = this.el.createChild({
28003 cls : 'roo-markdown-area'
28005 this.inputEl().addClass('d-none');
28006 if (this.getValue() == '') {
28007 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28010 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28012 this.markdownEl.on('click', this.toggleTextEdit, this);
28013 this.on('blur', this.toggleTextEdit, this);
28014 this.on('specialkey', this.resizeTextArea, this);
28017 toggleTextEdit : function()
28019 var sh = this.markdownEl.getHeight();
28020 this.inputEl().addClass('d-none');
28021 this.markdownEl.addClass('d-none');
28022 if (!this.editing) {
28024 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28025 this.inputEl().removeClass('d-none');
28026 this.inputEl().focus();
28027 this.editing = true;
28030 // show showdown...
28031 this.updateMarkdown();
28032 this.markdownEl.removeClass('d-none');
28033 this.editing = false;
28036 updateMarkdown : function()
28038 if (this.getValue() == '') {
28039 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28043 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28046 resizeTextArea: function () {
28049 Roo.log([sh, this.getValue().split("\n").length * 30]);
28050 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28052 setValue : function(val)
28054 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28055 if (!this.editing) {
28056 this.updateMarkdown();
28062 if (!this.editing) {
28063 this.toggleTextEdit();
28071 * Ext JS Library 1.1.1
28072 * Copyright(c) 2006-2007, Ext JS, LLC.
28074 * Originally Released Under LGPL - original licence link has changed is not relivant.
28077 * <script type="text/javascript">
28081 * @class Roo.bootstrap.PagingToolbar
28082 * @extends Roo.bootstrap.NavSimplebar
28083 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28085 * Create a new PagingToolbar
28086 * @param {Object} config The config object
28087 * @param {Roo.data.Store} store
28089 Roo.bootstrap.PagingToolbar = function(config)
28091 // old args format still supported... - xtype is prefered..
28092 // created from xtype...
28094 this.ds = config.dataSource;
28096 if (config.store && !this.ds) {
28097 this.store= Roo.factory(config.store, Roo.data);
28098 this.ds = this.store;
28099 this.ds.xmodule = this.xmodule || false;
28102 this.toolbarItems = [];
28103 if (config.items) {
28104 this.toolbarItems = config.items;
28107 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28112 this.bind(this.ds);
28115 if (Roo.bootstrap.version == 4) {
28116 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28118 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28123 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28125 * @cfg {Roo.data.Store} dataSource
28126 * The underlying data store providing the paged data
28129 * @cfg {String/HTMLElement/Element} container
28130 * container The id or element that will contain the toolbar
28133 * @cfg {Boolean} displayInfo
28134 * True to display the displayMsg (defaults to false)
28137 * @cfg {Number} pageSize
28138 * The number of records to display per page (defaults to 20)
28142 * @cfg {String} displayMsg
28143 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28145 displayMsg : 'Displaying {0} - {1} of {2}',
28147 * @cfg {String} emptyMsg
28148 * The message to display when no records are found (defaults to "No data to display")
28150 emptyMsg : 'No data to display',
28152 * Customizable piece of the default paging text (defaults to "Page")
28155 beforePageText : "Page",
28157 * Customizable piece of the default paging text (defaults to "of %0")
28160 afterPageText : "of {0}",
28162 * Customizable piece of the default paging text (defaults to "First Page")
28165 firstText : "First Page",
28167 * Customizable piece of the default paging text (defaults to "Previous Page")
28170 prevText : "Previous Page",
28172 * Customizable piece of the default paging text (defaults to "Next Page")
28175 nextText : "Next Page",
28177 * Customizable piece of the default paging text (defaults to "Last Page")
28180 lastText : "Last Page",
28182 * Customizable piece of the default paging text (defaults to "Refresh")
28185 refreshText : "Refresh",
28189 onRender : function(ct, position)
28191 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28192 this.navgroup.parentId = this.id;
28193 this.navgroup.onRender(this.el, null);
28194 // add the buttons to the navgroup
28196 if(this.displayInfo){
28197 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28198 this.displayEl = this.el.select('.x-paging-info', true).first();
28199 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28200 // this.displayEl = navel.el.select('span',true).first();
28206 Roo.each(_this.buttons, function(e){ // this might need to use render????
28207 Roo.factory(e).render(_this.el);
28211 Roo.each(_this.toolbarItems, function(e) {
28212 _this.navgroup.addItem(e);
28216 this.first = this.navgroup.addItem({
28217 tooltip: this.firstText,
28218 cls: "prev btn-outline-secondary",
28219 html : ' <i class="fa fa-step-backward"></i>',
28221 preventDefault: true,
28222 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28225 this.prev = this.navgroup.addItem({
28226 tooltip: this.prevText,
28227 cls: "prev btn-outline-secondary",
28228 html : ' <i class="fa fa-backward"></i>',
28230 preventDefault: true,
28231 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28233 //this.addSeparator();
28236 var field = this.navgroup.addItem( {
28238 cls : 'x-paging-position btn-outline-secondary',
28240 html : this.beforePageText +
28241 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28242 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28245 this.field = field.el.select('input', true).first();
28246 this.field.on("keydown", this.onPagingKeydown, this);
28247 this.field.on("focus", function(){this.dom.select();});
28250 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28251 //this.field.setHeight(18);
28252 //this.addSeparator();
28253 this.next = this.navgroup.addItem({
28254 tooltip: this.nextText,
28255 cls: "next btn-outline-secondary",
28256 html : ' <i class="fa fa-forward"></i>',
28258 preventDefault: true,
28259 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28261 this.last = this.navgroup.addItem({
28262 tooltip: this.lastText,
28263 html : ' <i class="fa fa-step-forward"></i>',
28264 cls: "next btn-outline-secondary",
28266 preventDefault: true,
28267 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28269 //this.addSeparator();
28270 this.loading = this.navgroup.addItem({
28271 tooltip: this.refreshText,
28272 cls: "btn-outline-secondary",
28273 html : ' <i class="fa fa-refresh"></i>',
28274 preventDefault: true,
28275 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28281 updateInfo : function(){
28282 if(this.displayEl){
28283 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28284 var msg = count == 0 ?
28288 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28290 this.displayEl.update(msg);
28295 onLoad : function(ds, r, o)
28297 this.cursor = o.params && o.params.start ? o.params.start : 0;
28299 var d = this.getPageData(),
28304 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28305 this.field.dom.value = ap;
28306 this.first.setDisabled(ap == 1);
28307 this.prev.setDisabled(ap == 1);
28308 this.next.setDisabled(ap == ps);
28309 this.last.setDisabled(ap == ps);
28310 this.loading.enable();
28315 getPageData : function(){
28316 var total = this.ds.getTotalCount();
28319 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28320 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28325 onLoadError : function(){
28326 this.loading.enable();
28330 onPagingKeydown : function(e){
28331 var k = e.getKey();
28332 var d = this.getPageData();
28334 var v = this.field.dom.value, pageNum;
28335 if(!v || isNaN(pageNum = parseInt(v, 10))){
28336 this.field.dom.value = d.activePage;
28339 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28340 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28343 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))
28345 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28346 this.field.dom.value = pageNum;
28347 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28350 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28352 var v = this.field.dom.value, pageNum;
28353 var increment = (e.shiftKey) ? 10 : 1;
28354 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28357 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28358 this.field.dom.value = d.activePage;
28361 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28363 this.field.dom.value = parseInt(v, 10) + increment;
28364 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28365 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28372 beforeLoad : function(){
28374 this.loading.disable();
28379 onClick : function(which){
28388 ds.load({params:{start: 0, limit: this.pageSize}});
28391 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28394 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28397 var total = ds.getTotalCount();
28398 var extra = total % this.pageSize;
28399 var lastStart = extra ? (total - extra) : total-this.pageSize;
28400 ds.load({params:{start: lastStart, limit: this.pageSize}});
28403 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28409 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28410 * @param {Roo.data.Store} store The data store to unbind
28412 unbind : function(ds){
28413 ds.un("beforeload", this.beforeLoad, this);
28414 ds.un("load", this.onLoad, this);
28415 ds.un("loadexception", this.onLoadError, this);
28416 ds.un("remove", this.updateInfo, this);
28417 ds.un("add", this.updateInfo, this);
28418 this.ds = undefined;
28422 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28423 * @param {Roo.data.Store} store The data store to bind
28425 bind : function(ds){
28426 ds.on("beforeload", this.beforeLoad, this);
28427 ds.on("load", this.onLoad, this);
28428 ds.on("loadexception", this.onLoadError, this);
28429 ds.on("remove", this.updateInfo, this);
28430 ds.on("add", this.updateInfo, this);
28441 * @class Roo.bootstrap.MessageBar
28442 * @extends Roo.bootstrap.Component
28443 * Bootstrap MessageBar class
28444 * @cfg {String} html contents of the MessageBar
28445 * @cfg {String} weight (info | success | warning | danger) default info
28446 * @cfg {String} beforeClass insert the bar before the given class
28447 * @cfg {Boolean} closable (true | false) default false
28448 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28451 * Create a new Element
28452 * @param {Object} config The config object
28455 Roo.bootstrap.MessageBar = function(config){
28456 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28459 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28465 beforeClass: 'bootstrap-sticky-wrap',
28467 getAutoCreate : function(){
28471 cls: 'alert alert-dismissable alert-' + this.weight,
28476 html: this.html || ''
28482 cfg.cls += ' alert-messages-fixed';
28496 onRender : function(ct, position)
28498 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28501 var cfg = Roo.apply({}, this.getAutoCreate());
28505 cfg.cls += ' ' + this.cls;
28508 cfg.style = this.style;
28510 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28512 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28515 this.el.select('>button.close').on('click', this.hide, this);
28521 if (!this.rendered) {
28527 this.fireEvent('show', this);
28533 if (!this.rendered) {
28539 this.fireEvent('hide', this);
28542 update : function()
28544 // var e = this.el.dom.firstChild;
28546 // if(this.closable){
28547 // e = e.nextSibling;
28550 // e.data = this.html || '';
28552 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28568 * @class Roo.bootstrap.Graph
28569 * @extends Roo.bootstrap.Component
28570 * Bootstrap Graph class
28574 @cfg {String} graphtype bar | vbar | pie
28575 @cfg {number} g_x coodinator | centre x (pie)
28576 @cfg {number} g_y coodinator | centre y (pie)
28577 @cfg {number} g_r radius (pie)
28578 @cfg {number} g_height height of the chart (respected by all elements in the set)
28579 @cfg {number} g_width width of the chart (respected by all elements in the set)
28580 @cfg {Object} title The title of the chart
28583 -opts (object) options for the chart
28585 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28586 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28588 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.
28589 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28591 o stretch (boolean)
28593 -opts (object) options for the pie
28596 o startAngle (number)
28597 o endAngle (number)
28601 * Create a new Input
28602 * @param {Object} config The config object
28605 Roo.bootstrap.Graph = function(config){
28606 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28612 * The img click event for the img.
28613 * @param {Roo.EventObject} e
28619 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28630 //g_colors: this.colors,
28637 getAutoCreate : function(){
28648 onRender : function(ct,position){
28651 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28653 if (typeof(Raphael) == 'undefined') {
28654 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28658 this.raphael = Raphael(this.el.dom);
28660 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28661 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28662 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28663 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28665 r.text(160, 10, "Single Series Chart").attr(txtattr);
28666 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28667 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28668 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28670 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28671 r.barchart(330, 10, 300, 220, data1);
28672 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28673 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28676 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28677 // r.barchart(30, 30, 560, 250, xdata, {
28678 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28679 // axis : "0 0 1 1",
28680 // axisxlabels : xdata
28681 // //yvalues : cols,
28684 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28686 // this.load(null,xdata,{
28687 // axis : "0 0 1 1",
28688 // axisxlabels : xdata
28693 load : function(graphtype,xdata,opts)
28695 this.raphael.clear();
28697 graphtype = this.graphtype;
28702 var r = this.raphael,
28703 fin = function () {
28704 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28706 fout = function () {
28707 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28709 pfin = function() {
28710 this.sector.stop();
28711 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28714 this.label[0].stop();
28715 this.label[0].attr({ r: 7.5 });
28716 this.label[1].attr({ "font-weight": 800 });
28719 pfout = function() {
28720 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28723 this.label[0].animate({ r: 5 }, 500, "bounce");
28724 this.label[1].attr({ "font-weight": 400 });
28730 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28733 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28736 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28737 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28739 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28746 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28751 setTitle: function(o)
28756 initEvents: function() {
28759 this.el.on('click', this.onClick, this);
28763 onClick : function(e)
28765 Roo.log('img onclick');
28766 this.fireEvent('click', this, e);
28778 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28781 * @class Roo.bootstrap.dash.NumberBox
28782 * @extends Roo.bootstrap.Component
28783 * Bootstrap NumberBox class
28784 * @cfg {String} headline Box headline
28785 * @cfg {String} content Box content
28786 * @cfg {String} icon Box icon
28787 * @cfg {String} footer Footer text
28788 * @cfg {String} fhref Footer href
28791 * Create a new NumberBox
28792 * @param {Object} config The config object
28796 Roo.bootstrap.dash.NumberBox = function(config){
28797 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28801 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28810 getAutoCreate : function(){
28814 cls : 'small-box ',
28822 cls : 'roo-headline',
28823 html : this.headline
28827 cls : 'roo-content',
28828 html : this.content
28842 cls : 'ion ' + this.icon
28851 cls : 'small-box-footer',
28852 href : this.fhref || '#',
28856 cfg.cn.push(footer);
28863 onRender : function(ct,position){
28864 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28871 setHeadline: function (value)
28873 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28876 setFooter: function (value, href)
28878 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28881 this.el.select('a.small-box-footer',true).first().attr('href', href);
28886 setContent: function (value)
28888 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28891 initEvents: function()
28905 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28908 * @class Roo.bootstrap.dash.TabBox
28909 * @extends Roo.bootstrap.Component
28910 * Bootstrap TabBox class
28911 * @cfg {String} title Title of the TabBox
28912 * @cfg {String} icon Icon of the TabBox
28913 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28914 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28917 * Create a new TabBox
28918 * @param {Object} config The config object
28922 Roo.bootstrap.dash.TabBox = function(config){
28923 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28928 * When a pane is added
28929 * @param {Roo.bootstrap.dash.TabPane} pane
28933 * @event activatepane
28934 * When a pane is activated
28935 * @param {Roo.bootstrap.dash.TabPane} pane
28937 "activatepane" : true
28945 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28950 tabScrollable : false,
28952 getChildContainer : function()
28954 return this.el.select('.tab-content', true).first();
28957 getAutoCreate : function(){
28961 cls: 'pull-left header',
28969 cls: 'fa ' + this.icon
28975 cls: 'nav nav-tabs pull-right',
28981 if(this.tabScrollable){
28988 cls: 'nav nav-tabs pull-right',
28999 cls: 'nav-tabs-custom',
29004 cls: 'tab-content no-padding',
29012 initEvents : function()
29014 //Roo.log('add add pane handler');
29015 this.on('addpane', this.onAddPane, this);
29018 * Updates the box title
29019 * @param {String} html to set the title to.
29021 setTitle : function(value)
29023 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29025 onAddPane : function(pane)
29027 this.panes.push(pane);
29028 //Roo.log('addpane');
29030 // tabs are rendere left to right..
29031 if(!this.showtabs){
29035 var ctr = this.el.select('.nav-tabs', true).first();
29038 var existing = ctr.select('.nav-tab',true);
29039 var qty = existing.getCount();;
29042 var tab = ctr.createChild({
29044 cls : 'nav-tab' + (qty ? '' : ' active'),
29052 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29055 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29057 pane.el.addClass('active');
29062 onTabClick : function(ev,un,ob,pane)
29064 //Roo.log('tab - prev default');
29065 ev.preventDefault();
29068 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29069 pane.tab.addClass('active');
29070 //Roo.log(pane.title);
29071 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29072 // technically we should have a deactivate event.. but maybe add later.
29073 // and it should not de-activate the selected tab...
29074 this.fireEvent('activatepane', pane);
29075 pane.el.addClass('active');
29076 pane.fireEvent('activate');
29081 getActivePane : function()
29084 Roo.each(this.panes, function(p) {
29085 if(p.el.hasClass('active')){
29106 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29108 * @class Roo.bootstrap.TabPane
29109 * @extends Roo.bootstrap.Component
29110 * Bootstrap TabPane class
29111 * @cfg {Boolean} active (false | true) Default false
29112 * @cfg {String} title title of panel
29116 * Create a new TabPane
29117 * @param {Object} config The config object
29120 Roo.bootstrap.dash.TabPane = function(config){
29121 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29127 * When a pane is activated
29128 * @param {Roo.bootstrap.dash.TabPane} pane
29135 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29140 // the tabBox that this is attached to.
29143 getAutoCreate : function()
29151 cfg.cls += ' active';
29156 initEvents : function()
29158 //Roo.log('trigger add pane handler');
29159 this.parent().fireEvent('addpane', this)
29163 * Updates the tab title
29164 * @param {String} html to set the title to.
29166 setTitle: function(str)
29172 this.tab.select('a', true).first().dom.innerHTML = str;
29189 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29192 * @class Roo.bootstrap.menu.Menu
29193 * @extends Roo.bootstrap.Component
29194 * Bootstrap Menu class - container for Menu
29195 * @cfg {String} html Text of the menu
29196 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29197 * @cfg {String} icon Font awesome icon
29198 * @cfg {String} pos Menu align to (top | bottom) default bottom
29202 * Create a new Menu
29203 * @param {Object} config The config object
29207 Roo.bootstrap.menu.Menu = function(config){
29208 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29212 * @event beforeshow
29213 * Fires before this menu is displayed
29214 * @param {Roo.bootstrap.menu.Menu} this
29218 * @event beforehide
29219 * Fires before this menu is hidden
29220 * @param {Roo.bootstrap.menu.Menu} this
29225 * Fires after this menu is displayed
29226 * @param {Roo.bootstrap.menu.Menu} this
29231 * Fires after this menu is hidden
29232 * @param {Roo.bootstrap.menu.Menu} this
29237 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29238 * @param {Roo.bootstrap.menu.Menu} this
29239 * @param {Roo.EventObject} e
29246 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29250 weight : 'default',
29255 getChildContainer : function() {
29256 if(this.isSubMenu){
29260 return this.el.select('ul.dropdown-menu', true).first();
29263 getAutoCreate : function()
29268 cls : 'roo-menu-text',
29276 cls : 'fa ' + this.icon
29287 cls : 'dropdown-button btn btn-' + this.weight,
29292 cls : 'dropdown-toggle btn btn-' + this.weight,
29302 cls : 'dropdown-menu'
29308 if(this.pos == 'top'){
29309 cfg.cls += ' dropup';
29312 if(this.isSubMenu){
29315 cls : 'dropdown-menu'
29322 onRender : function(ct, position)
29324 this.isSubMenu = ct.hasClass('dropdown-submenu');
29326 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29329 initEvents : function()
29331 if(this.isSubMenu){
29335 this.hidden = true;
29337 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29338 this.triggerEl.on('click', this.onTriggerPress, this);
29340 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29341 this.buttonEl.on('click', this.onClick, this);
29347 if(this.isSubMenu){
29351 return this.el.select('ul.dropdown-menu', true).first();
29354 onClick : function(e)
29356 this.fireEvent("click", this, e);
29359 onTriggerPress : function(e)
29361 if (this.isVisible()) {
29368 isVisible : function(){
29369 return !this.hidden;
29374 this.fireEvent("beforeshow", this);
29376 this.hidden = false;
29377 this.el.addClass('open');
29379 Roo.get(document).on("mouseup", this.onMouseUp, this);
29381 this.fireEvent("show", this);
29388 this.fireEvent("beforehide", this);
29390 this.hidden = true;
29391 this.el.removeClass('open');
29393 Roo.get(document).un("mouseup", this.onMouseUp);
29395 this.fireEvent("hide", this);
29398 onMouseUp : function()
29412 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29415 * @class Roo.bootstrap.menu.Item
29416 * @extends Roo.bootstrap.Component
29417 * Bootstrap MenuItem class
29418 * @cfg {Boolean} submenu (true | false) default false
29419 * @cfg {String} html text of the item
29420 * @cfg {String} href the link
29421 * @cfg {Boolean} disable (true | false) default false
29422 * @cfg {Boolean} preventDefault (true | false) default true
29423 * @cfg {String} icon Font awesome icon
29424 * @cfg {String} pos Submenu align to (left | right) default right
29428 * Create a new Item
29429 * @param {Object} config The config object
29433 Roo.bootstrap.menu.Item = function(config){
29434 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29438 * Fires when the mouse is hovering over this menu
29439 * @param {Roo.bootstrap.menu.Item} this
29440 * @param {Roo.EventObject} e
29445 * Fires when the mouse exits this menu
29446 * @param {Roo.bootstrap.menu.Item} this
29447 * @param {Roo.EventObject} e
29453 * The raw click event for the entire grid.
29454 * @param {Roo.EventObject} e
29460 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29465 preventDefault: true,
29470 getAutoCreate : function()
29475 cls : 'roo-menu-item-text',
29483 cls : 'fa ' + this.icon
29492 href : this.href || '#',
29499 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29503 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29505 if(this.pos == 'left'){
29506 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29513 initEvents : function()
29515 this.el.on('mouseover', this.onMouseOver, this);
29516 this.el.on('mouseout', this.onMouseOut, this);
29518 this.el.select('a', true).first().on('click', this.onClick, this);
29522 onClick : function(e)
29524 if(this.preventDefault){
29525 e.preventDefault();
29528 this.fireEvent("click", this, e);
29531 onMouseOver : function(e)
29533 if(this.submenu && this.pos == 'left'){
29534 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29537 this.fireEvent("mouseover", this, e);
29540 onMouseOut : function(e)
29542 this.fireEvent("mouseout", this, e);
29554 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29557 * @class Roo.bootstrap.menu.Separator
29558 * @extends Roo.bootstrap.Component
29559 * Bootstrap Separator class
29562 * Create a new Separator
29563 * @param {Object} config The config object
29567 Roo.bootstrap.menu.Separator = function(config){
29568 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29571 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29573 getAutoCreate : function(){
29576 cls: 'dropdown-divider divider'
29594 * @class Roo.bootstrap.Tooltip
29595 * Bootstrap Tooltip class
29596 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29597 * to determine which dom element triggers the tooltip.
29599 * It needs to add support for additional attributes like tooltip-position
29602 * Create a new Toolti
29603 * @param {Object} config The config object
29606 Roo.bootstrap.Tooltip = function(config){
29607 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29609 this.alignment = Roo.bootstrap.Tooltip.alignment;
29611 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29612 this.alignment = config.alignment;
29617 Roo.apply(Roo.bootstrap.Tooltip, {
29619 * @function init initialize tooltip monitoring.
29623 currentTip : false,
29624 currentRegion : false,
29630 Roo.get(document).on('mouseover', this.enter ,this);
29631 Roo.get(document).on('mouseout', this.leave, this);
29634 this.currentTip = new Roo.bootstrap.Tooltip();
29637 enter : function(ev)
29639 var dom = ev.getTarget();
29641 //Roo.log(['enter',dom]);
29642 var el = Roo.fly(dom);
29643 if (this.currentEl) {
29645 //Roo.log(this.currentEl);
29646 //Roo.log(this.currentEl.contains(dom));
29647 if (this.currentEl == el) {
29650 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29656 if (this.currentTip.el) {
29657 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29661 if(!el || el.dom == document){
29667 if (!el.attr('tooltip')) {
29668 pel = el.findParent("[tooltip]");
29670 bindEl = Roo.get(pel);
29676 // you can not look for children, as if el is the body.. then everythign is the child..
29677 if (!pel && !el.attr('tooltip')) { //
29678 if (!el.select("[tooltip]").elements.length) {
29681 // is the mouse over this child...?
29682 bindEl = el.select("[tooltip]").first();
29683 var xy = ev.getXY();
29684 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29685 //Roo.log("not in region.");
29688 //Roo.log("child element over..");
29691 this.currentEl = el;
29692 this.currentTip.bind(bindEl);
29693 this.currentRegion = Roo.lib.Region.getRegion(dom);
29694 this.currentTip.enter();
29697 leave : function(ev)
29699 var dom = ev.getTarget();
29700 //Roo.log(['leave',dom]);
29701 if (!this.currentEl) {
29706 if (dom != this.currentEl.dom) {
29709 var xy = ev.getXY();
29710 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29713 // only activate leave if mouse cursor is outside... bounding box..
29718 if (this.currentTip) {
29719 this.currentTip.leave();
29721 //Roo.log('clear currentEl');
29722 this.currentEl = false;
29727 'left' : ['r-l', [-2,0], 'right'],
29728 'right' : ['l-r', [2,0], 'left'],
29729 'bottom' : ['t-b', [0,2], 'top'],
29730 'top' : [ 'b-t', [0,-2], 'bottom']
29736 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29741 delay : null, // can be { show : 300 , hide: 500}
29745 hoverState : null, //???
29747 placement : 'bottom',
29751 getAutoCreate : function(){
29758 cls : 'tooltip-arrow arrow'
29761 cls : 'tooltip-inner'
29768 bind : function(el)
29773 initEvents : function()
29775 this.arrowEl = this.el.select('.arrow', true).first();
29776 this.innerEl = this.el.select('.tooltip-inner', true).first();
29779 enter : function () {
29781 if (this.timeout != null) {
29782 clearTimeout(this.timeout);
29785 this.hoverState = 'in';
29786 //Roo.log("enter - show");
29787 if (!this.delay || !this.delay.show) {
29792 this.timeout = setTimeout(function () {
29793 if (_t.hoverState == 'in') {
29796 }, this.delay.show);
29800 clearTimeout(this.timeout);
29802 this.hoverState = 'out';
29803 if (!this.delay || !this.delay.hide) {
29809 this.timeout = setTimeout(function () {
29810 //Roo.log("leave - timeout");
29812 if (_t.hoverState == 'out') {
29814 Roo.bootstrap.Tooltip.currentEl = false;
29819 show : function (msg)
29822 this.render(document.body);
29825 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29827 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29829 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29831 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29832 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29834 var placement = typeof this.placement == 'function' ?
29835 this.placement.call(this, this.el, on_el) :
29838 var autoToken = /\s?auto?\s?/i;
29839 var autoPlace = autoToken.test(placement);
29841 placement = placement.replace(autoToken, '') || 'top';
29845 //this.el.setXY([0,0]);
29847 //this.el.dom.style.display='block';
29849 //this.el.appendTo(on_el);
29851 var p = this.getPosition();
29852 var box = this.el.getBox();
29858 var align = this.alignment[placement];
29860 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29862 if(placement == 'top' || placement == 'bottom'){
29864 placement = 'right';
29867 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29868 placement = 'left';
29871 var scroll = Roo.select('body', true).first().getScroll();
29873 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29877 align = this.alignment[placement];
29879 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29883 var elems = document.getElementsByTagName('div');
29884 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29885 for (var i = 0; i < elems.length; i++) {
29886 var zindex = Number.parseInt(
29887 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29890 if (zindex > highest) {
29897 this.el.dom.style.zIndex = highest;
29899 this.el.alignTo(this.bindEl, align[0],align[1]);
29900 //var arrow = this.el.select('.arrow',true).first();
29901 //arrow.set(align[2],
29903 this.el.addClass(placement);
29904 this.el.addClass("bs-tooltip-"+ placement);
29906 this.el.addClass('in fade show');
29908 this.hoverState = null;
29910 if (this.el.hasClass('fade')) {
29925 //this.el.setXY([0,0]);
29926 this.el.removeClass(['show', 'in']);
29942 * @class Roo.bootstrap.LocationPicker
29943 * @extends Roo.bootstrap.Component
29944 * Bootstrap LocationPicker class
29945 * @cfg {Number} latitude Position when init default 0
29946 * @cfg {Number} longitude Position when init default 0
29947 * @cfg {Number} zoom default 15
29948 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29949 * @cfg {Boolean} mapTypeControl default false
29950 * @cfg {Boolean} disableDoubleClickZoom default false
29951 * @cfg {Boolean} scrollwheel default true
29952 * @cfg {Boolean} streetViewControl default false
29953 * @cfg {Number} radius default 0
29954 * @cfg {String} locationName
29955 * @cfg {Boolean} draggable default true
29956 * @cfg {Boolean} enableAutocomplete default false
29957 * @cfg {Boolean} enableReverseGeocode default true
29958 * @cfg {String} markerTitle
29961 * Create a new LocationPicker
29962 * @param {Object} config The config object
29966 Roo.bootstrap.LocationPicker = function(config){
29968 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29973 * Fires when the picker initialized.
29974 * @param {Roo.bootstrap.LocationPicker} this
29975 * @param {Google Location} location
29979 * @event positionchanged
29980 * Fires when the picker position changed.
29981 * @param {Roo.bootstrap.LocationPicker} this
29982 * @param {Google Location} location
29984 positionchanged : true,
29987 * Fires when the map resize.
29988 * @param {Roo.bootstrap.LocationPicker} this
29993 * Fires when the map show.
29994 * @param {Roo.bootstrap.LocationPicker} this
29999 * Fires when the map hide.
30000 * @param {Roo.bootstrap.LocationPicker} this
30005 * Fires when click the map.
30006 * @param {Roo.bootstrap.LocationPicker} this
30007 * @param {Map event} e
30011 * @event mapRightClick
30012 * Fires when right click the map.
30013 * @param {Roo.bootstrap.LocationPicker} this
30014 * @param {Map event} e
30016 mapRightClick : true,
30018 * @event markerClick
30019 * Fires when click the marker.
30020 * @param {Roo.bootstrap.LocationPicker} this
30021 * @param {Map event} e
30023 markerClick : true,
30025 * @event markerRightClick
30026 * Fires when right click the marker.
30027 * @param {Roo.bootstrap.LocationPicker} this
30028 * @param {Map event} e
30030 markerRightClick : true,
30032 * @event OverlayViewDraw
30033 * Fires when OverlayView Draw
30034 * @param {Roo.bootstrap.LocationPicker} this
30036 OverlayViewDraw : true,
30038 * @event OverlayViewOnAdd
30039 * Fires when OverlayView Draw
30040 * @param {Roo.bootstrap.LocationPicker} this
30042 OverlayViewOnAdd : true,
30044 * @event OverlayViewOnRemove
30045 * Fires when OverlayView Draw
30046 * @param {Roo.bootstrap.LocationPicker} this
30048 OverlayViewOnRemove : true,
30050 * @event OverlayViewShow
30051 * Fires when OverlayView Draw
30052 * @param {Roo.bootstrap.LocationPicker} this
30053 * @param {Pixel} cpx
30055 OverlayViewShow : true,
30057 * @event OverlayViewHide
30058 * Fires when OverlayView Draw
30059 * @param {Roo.bootstrap.LocationPicker} this
30061 OverlayViewHide : true,
30063 * @event loadexception
30064 * Fires when load google lib failed.
30065 * @param {Roo.bootstrap.LocationPicker} this
30067 loadexception : true
30072 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30074 gMapContext: false,
30080 mapTypeControl: false,
30081 disableDoubleClickZoom: false,
30083 streetViewControl: false,
30087 enableAutocomplete: false,
30088 enableReverseGeocode: true,
30091 getAutoCreate: function()
30096 cls: 'roo-location-picker'
30102 initEvents: function(ct, position)
30104 if(!this.el.getWidth() || this.isApplied()){
30108 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30113 initial: function()
30115 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30116 this.fireEvent('loadexception', this);
30120 if(!this.mapTypeId){
30121 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30124 this.gMapContext = this.GMapContext();
30126 this.initOverlayView();
30128 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30132 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30133 _this.setPosition(_this.gMapContext.marker.position);
30136 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30137 _this.fireEvent('mapClick', this, event);
30141 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30142 _this.fireEvent('mapRightClick', this, event);
30146 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30147 _this.fireEvent('markerClick', this, event);
30151 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30152 _this.fireEvent('markerRightClick', this, event);
30156 this.setPosition(this.gMapContext.location);
30158 this.fireEvent('initial', this, this.gMapContext.location);
30161 initOverlayView: function()
30165 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30169 _this.fireEvent('OverlayViewDraw', _this);
30174 _this.fireEvent('OverlayViewOnAdd', _this);
30177 onRemove: function()
30179 _this.fireEvent('OverlayViewOnRemove', _this);
30182 show: function(cpx)
30184 _this.fireEvent('OverlayViewShow', _this, cpx);
30189 _this.fireEvent('OverlayViewHide', _this);
30195 fromLatLngToContainerPixel: function(event)
30197 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30200 isApplied: function()
30202 return this.getGmapContext() == false ? false : true;
30205 getGmapContext: function()
30207 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30210 GMapContext: function()
30212 var position = new google.maps.LatLng(this.latitude, this.longitude);
30214 var _map = new google.maps.Map(this.el.dom, {
30217 mapTypeId: this.mapTypeId,
30218 mapTypeControl: this.mapTypeControl,
30219 disableDoubleClickZoom: this.disableDoubleClickZoom,
30220 scrollwheel: this.scrollwheel,
30221 streetViewControl: this.streetViewControl,
30222 locationName: this.locationName,
30223 draggable: this.draggable,
30224 enableAutocomplete: this.enableAutocomplete,
30225 enableReverseGeocode: this.enableReverseGeocode
30228 var _marker = new google.maps.Marker({
30229 position: position,
30231 title: this.markerTitle,
30232 draggable: this.draggable
30239 location: position,
30240 radius: this.radius,
30241 locationName: this.locationName,
30242 addressComponents: {
30243 formatted_address: null,
30244 addressLine1: null,
30245 addressLine2: null,
30247 streetNumber: null,
30251 stateOrProvince: null
30254 domContainer: this.el.dom,
30255 geodecoder: new google.maps.Geocoder()
30259 drawCircle: function(center, radius, options)
30261 if (this.gMapContext.circle != null) {
30262 this.gMapContext.circle.setMap(null);
30266 options = Roo.apply({}, options, {
30267 strokeColor: "#0000FF",
30268 strokeOpacity: .35,
30270 fillColor: "#0000FF",
30274 options.map = this.gMapContext.map;
30275 options.radius = radius;
30276 options.center = center;
30277 this.gMapContext.circle = new google.maps.Circle(options);
30278 return this.gMapContext.circle;
30284 setPosition: function(location)
30286 this.gMapContext.location = location;
30287 this.gMapContext.marker.setPosition(location);
30288 this.gMapContext.map.panTo(location);
30289 this.drawCircle(location, this.gMapContext.radius, {});
30293 if (this.gMapContext.settings.enableReverseGeocode) {
30294 this.gMapContext.geodecoder.geocode({
30295 latLng: this.gMapContext.location
30296 }, function(results, status) {
30298 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30299 _this.gMapContext.locationName = results[0].formatted_address;
30300 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30302 _this.fireEvent('positionchanged', this, location);
30309 this.fireEvent('positionchanged', this, location);
30314 google.maps.event.trigger(this.gMapContext.map, "resize");
30316 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30318 this.fireEvent('resize', this);
30321 setPositionByLatLng: function(latitude, longitude)
30323 this.setPosition(new google.maps.LatLng(latitude, longitude));
30326 getCurrentPosition: function()
30329 latitude: this.gMapContext.location.lat(),
30330 longitude: this.gMapContext.location.lng()
30334 getAddressName: function()
30336 return this.gMapContext.locationName;
30339 getAddressComponents: function()
30341 return this.gMapContext.addressComponents;
30344 address_component_from_google_geocode: function(address_components)
30348 for (var i = 0; i < address_components.length; i++) {
30349 var component = address_components[i];
30350 if (component.types.indexOf("postal_code") >= 0) {
30351 result.postalCode = component.short_name;
30352 } else if (component.types.indexOf("street_number") >= 0) {
30353 result.streetNumber = component.short_name;
30354 } else if (component.types.indexOf("route") >= 0) {
30355 result.streetName = component.short_name;
30356 } else if (component.types.indexOf("neighborhood") >= 0) {
30357 result.city = component.short_name;
30358 } else if (component.types.indexOf("locality") >= 0) {
30359 result.city = component.short_name;
30360 } else if (component.types.indexOf("sublocality") >= 0) {
30361 result.district = component.short_name;
30362 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30363 result.stateOrProvince = component.short_name;
30364 } else if (component.types.indexOf("country") >= 0) {
30365 result.country = component.short_name;
30369 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30370 result.addressLine2 = "";
30374 setZoomLevel: function(zoom)
30376 this.gMapContext.map.setZoom(zoom);
30389 this.fireEvent('show', this);
30400 this.fireEvent('hide', this);
30405 Roo.apply(Roo.bootstrap.LocationPicker, {
30407 OverlayView : function(map, options)
30409 options = options || {};
30416 * @class Roo.bootstrap.Alert
30417 * @extends Roo.bootstrap.Component
30418 * Bootstrap Alert class - shows an alert area box
30420 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30421 Enter a valid email address
30424 * @cfg {String} title The title of alert
30425 * @cfg {String} html The content of alert
30426 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30427 * @cfg {String} fa font-awesomeicon
30428 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30429 * @cfg {Boolean} close true to show a x closer
30433 * Create a new alert
30434 * @param {Object} config The config object
30438 Roo.bootstrap.Alert = function(config){
30439 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30443 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30449 faicon: false, // BC
30453 getAutoCreate : function()
30465 style : this.close ? '' : 'display:none'
30469 cls : 'roo-alert-icon'
30474 cls : 'roo-alert-title',
30479 cls : 'roo-alert-text',
30486 cfg.cn[0].cls += ' fa ' + this.faicon;
30489 cfg.cn[0].cls += ' fa ' + this.fa;
30493 cfg.cls += ' alert-' + this.weight;
30499 initEvents: function()
30501 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30502 this.titleEl = this.el.select('.roo-alert-title',true).first();
30503 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30504 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30505 if (this.seconds > 0) {
30506 this.hide.defer(this.seconds, this);
30510 * Set the Title Message HTML
30511 * @param {String} html
30513 setTitle : function(str)
30515 this.titleEl.dom.innerHTML = str;
30519 * Set the Body Message HTML
30520 * @param {String} html
30522 setHtml : function(str)
30524 this.htmlEl.dom.innerHTML = str;
30527 * Set the Weight of the alert
30528 * @param {String} (success|info|warning|danger) weight
30531 setWeight : function(weight)
30534 this.el.removeClass('alert-' + this.weight);
30537 this.weight = weight;
30539 this.el.addClass('alert-' + this.weight);
30542 * Set the Icon of the alert
30543 * @param {String} see fontawsome names (name without the 'fa-' bit)
30545 setIcon : function(icon)
30548 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30551 this.faicon = icon;
30553 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30578 * @class Roo.bootstrap.UploadCropbox
30579 * @extends Roo.bootstrap.Component
30580 * Bootstrap UploadCropbox class
30581 * @cfg {String} emptyText show when image has been loaded
30582 * @cfg {String} rotateNotify show when image too small to rotate
30583 * @cfg {Number} errorTimeout default 3000
30584 * @cfg {Number} minWidth default 300
30585 * @cfg {Number} minHeight default 300
30586 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30587 * @cfg {Boolean} isDocument (true|false) default false
30588 * @cfg {String} url action url
30589 * @cfg {String} paramName default 'imageUpload'
30590 * @cfg {String} method default POST
30591 * @cfg {Boolean} loadMask (true|false) default true
30592 * @cfg {Boolean} loadingText default 'Loading...'
30595 * Create a new UploadCropbox
30596 * @param {Object} config The config object
30599 Roo.bootstrap.UploadCropbox = function(config){
30600 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30604 * @event beforeselectfile
30605 * Fire before select file
30606 * @param {Roo.bootstrap.UploadCropbox} this
30608 "beforeselectfile" : true,
30611 * Fire after initEvent
30612 * @param {Roo.bootstrap.UploadCropbox} this
30617 * Fire after initEvent
30618 * @param {Roo.bootstrap.UploadCropbox} this
30619 * @param {String} data
30624 * Fire when preparing the file data
30625 * @param {Roo.bootstrap.UploadCropbox} this
30626 * @param {Object} file
30631 * Fire when get exception
30632 * @param {Roo.bootstrap.UploadCropbox} this
30633 * @param {XMLHttpRequest} xhr
30635 "exception" : true,
30637 * @event beforeloadcanvas
30638 * Fire before load the canvas
30639 * @param {Roo.bootstrap.UploadCropbox} this
30640 * @param {String} src
30642 "beforeloadcanvas" : true,
30645 * Fire when trash image
30646 * @param {Roo.bootstrap.UploadCropbox} this
30651 * Fire when download the image
30652 * @param {Roo.bootstrap.UploadCropbox} this
30656 * @event footerbuttonclick
30657 * Fire when footerbuttonclick
30658 * @param {Roo.bootstrap.UploadCropbox} this
30659 * @param {String} type
30661 "footerbuttonclick" : true,
30665 * @param {Roo.bootstrap.UploadCropbox} this
30670 * Fire when rotate the image
30671 * @param {Roo.bootstrap.UploadCropbox} this
30672 * @param {String} pos
30677 * Fire when inspect the file
30678 * @param {Roo.bootstrap.UploadCropbox} this
30679 * @param {Object} file
30684 * Fire when xhr upload the file
30685 * @param {Roo.bootstrap.UploadCropbox} this
30686 * @param {Object} data
30691 * Fire when arrange the file data
30692 * @param {Roo.bootstrap.UploadCropbox} this
30693 * @param {Object} formData
30698 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30701 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30703 emptyText : 'Click to upload image',
30704 rotateNotify : 'Image is too small to rotate',
30705 errorTimeout : 3000,
30719 cropType : 'image/jpeg',
30721 canvasLoaded : false,
30722 isDocument : false,
30724 paramName : 'imageUpload',
30726 loadingText : 'Loading...',
30729 getAutoCreate : function()
30733 cls : 'roo-upload-cropbox',
30737 cls : 'roo-upload-cropbox-selector',
30742 cls : 'roo-upload-cropbox-body',
30743 style : 'cursor:pointer',
30747 cls : 'roo-upload-cropbox-preview'
30751 cls : 'roo-upload-cropbox-thumb'
30755 cls : 'roo-upload-cropbox-empty-notify',
30756 html : this.emptyText
30760 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30761 html : this.rotateNotify
30767 cls : 'roo-upload-cropbox-footer',
30770 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30780 onRender : function(ct, position)
30782 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30784 if (this.buttons.length) {
30786 Roo.each(this.buttons, function(bb) {
30788 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30790 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30796 this.maskEl = this.el;
30800 initEvents : function()
30802 this.urlAPI = (window.createObjectURL && window) ||
30803 (window.URL && URL.revokeObjectURL && URL) ||
30804 (window.webkitURL && webkitURL);
30806 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30807 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30809 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30810 this.selectorEl.hide();
30812 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30813 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30815 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30816 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30817 this.thumbEl.hide();
30819 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30820 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30822 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30823 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30824 this.errorEl.hide();
30826 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30827 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30828 this.footerEl.hide();
30830 this.setThumbBoxSize();
30836 this.fireEvent('initial', this);
30843 window.addEventListener("resize", function() { _this.resize(); } );
30845 this.bodyEl.on('click', this.beforeSelectFile, this);
30848 this.bodyEl.on('touchstart', this.onTouchStart, this);
30849 this.bodyEl.on('touchmove', this.onTouchMove, this);
30850 this.bodyEl.on('touchend', this.onTouchEnd, this);
30854 this.bodyEl.on('mousedown', this.onMouseDown, this);
30855 this.bodyEl.on('mousemove', this.onMouseMove, this);
30856 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30857 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30858 Roo.get(document).on('mouseup', this.onMouseUp, this);
30861 this.selectorEl.on('change', this.onFileSelected, this);
30867 this.baseScale = 1;
30869 this.baseRotate = 1;
30870 this.dragable = false;
30871 this.pinching = false;
30874 this.cropData = false;
30875 this.notifyEl.dom.innerHTML = this.emptyText;
30877 this.selectorEl.dom.value = '';
30881 resize : function()
30883 if(this.fireEvent('resize', this) != false){
30884 this.setThumbBoxPosition();
30885 this.setCanvasPosition();
30889 onFooterButtonClick : function(e, el, o, type)
30892 case 'rotate-left' :
30893 this.onRotateLeft(e);
30895 case 'rotate-right' :
30896 this.onRotateRight(e);
30899 this.beforeSelectFile(e);
30914 this.fireEvent('footerbuttonclick', this, type);
30917 beforeSelectFile : function(e)
30919 e.preventDefault();
30921 if(this.fireEvent('beforeselectfile', this) != false){
30922 this.selectorEl.dom.click();
30926 onFileSelected : function(e)
30928 e.preventDefault();
30930 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30934 var file = this.selectorEl.dom.files[0];
30936 if(this.fireEvent('inspect', this, file) != false){
30937 this.prepare(file);
30942 trash : function(e)
30944 this.fireEvent('trash', this);
30947 download : function(e)
30949 this.fireEvent('download', this);
30952 loadCanvas : function(src)
30954 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30958 this.imageEl = document.createElement('img');
30962 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30964 this.imageEl.src = src;
30968 onLoadCanvas : function()
30970 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30971 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30973 this.bodyEl.un('click', this.beforeSelectFile, this);
30975 this.notifyEl.hide();
30976 this.thumbEl.show();
30977 this.footerEl.show();
30979 this.baseRotateLevel();
30981 if(this.isDocument){
30982 this.setThumbBoxSize();
30985 this.setThumbBoxPosition();
30987 this.baseScaleLevel();
30993 this.canvasLoaded = true;
30996 this.maskEl.unmask();
31001 setCanvasPosition : function()
31003 if(!this.canvasEl){
31007 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31008 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31010 this.previewEl.setLeft(pw);
31011 this.previewEl.setTop(ph);
31015 onMouseDown : function(e)
31019 this.dragable = true;
31020 this.pinching = false;
31022 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31023 this.dragable = false;
31027 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31028 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31032 onMouseMove : function(e)
31036 if(!this.canvasLoaded){
31040 if (!this.dragable){
31044 var minX = Math.ceil(this.thumbEl.getLeft(true));
31045 var minY = Math.ceil(this.thumbEl.getTop(true));
31047 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31048 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31050 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31051 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31053 x = x - this.mouseX;
31054 y = y - this.mouseY;
31056 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31057 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31059 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31060 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31062 this.previewEl.setLeft(bgX);
31063 this.previewEl.setTop(bgY);
31065 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31066 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31069 onMouseUp : function(e)
31073 this.dragable = false;
31076 onMouseWheel : function(e)
31080 this.startScale = this.scale;
31082 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31084 if(!this.zoomable()){
31085 this.scale = this.startScale;
31094 zoomable : function()
31096 var minScale = this.thumbEl.getWidth() / this.minWidth;
31098 if(this.minWidth < this.minHeight){
31099 minScale = this.thumbEl.getHeight() / this.minHeight;
31102 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31103 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31107 (this.rotate == 0 || this.rotate == 180) &&
31109 width > this.imageEl.OriginWidth ||
31110 height > this.imageEl.OriginHeight ||
31111 (width < this.minWidth && height < this.minHeight)
31119 (this.rotate == 90 || this.rotate == 270) &&
31121 width > this.imageEl.OriginWidth ||
31122 height > this.imageEl.OriginHeight ||
31123 (width < this.minHeight && height < this.minWidth)
31130 !this.isDocument &&
31131 (this.rotate == 0 || this.rotate == 180) &&
31133 width < this.minWidth ||
31134 width > this.imageEl.OriginWidth ||
31135 height < this.minHeight ||
31136 height > this.imageEl.OriginHeight
31143 !this.isDocument &&
31144 (this.rotate == 90 || this.rotate == 270) &&
31146 width < this.minHeight ||
31147 width > this.imageEl.OriginWidth ||
31148 height < this.minWidth ||
31149 height > this.imageEl.OriginHeight
31159 onRotateLeft : function(e)
31161 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31163 var minScale = this.thumbEl.getWidth() / this.minWidth;
31165 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31166 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31168 this.startScale = this.scale;
31170 while (this.getScaleLevel() < minScale){
31172 this.scale = this.scale + 1;
31174 if(!this.zoomable()){
31179 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31180 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31185 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31192 this.scale = this.startScale;
31194 this.onRotateFail();
31199 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31201 if(this.isDocument){
31202 this.setThumbBoxSize();
31203 this.setThumbBoxPosition();
31204 this.setCanvasPosition();
31209 this.fireEvent('rotate', this, 'left');
31213 onRotateRight : function(e)
31215 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31217 var minScale = this.thumbEl.getWidth() / this.minWidth;
31219 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31220 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31222 this.startScale = this.scale;
31224 while (this.getScaleLevel() < minScale){
31226 this.scale = this.scale + 1;
31228 if(!this.zoomable()){
31233 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31234 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31239 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31246 this.scale = this.startScale;
31248 this.onRotateFail();
31253 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31255 if(this.isDocument){
31256 this.setThumbBoxSize();
31257 this.setThumbBoxPosition();
31258 this.setCanvasPosition();
31263 this.fireEvent('rotate', this, 'right');
31266 onRotateFail : function()
31268 this.errorEl.show(true);
31272 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31277 this.previewEl.dom.innerHTML = '';
31279 var canvasEl = document.createElement("canvas");
31281 var contextEl = canvasEl.getContext("2d");
31283 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31284 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31285 var center = this.imageEl.OriginWidth / 2;
31287 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31288 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31289 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31290 center = this.imageEl.OriginHeight / 2;
31293 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31295 contextEl.translate(center, center);
31296 contextEl.rotate(this.rotate * Math.PI / 180);
31298 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31300 this.canvasEl = document.createElement("canvas");
31302 this.contextEl = this.canvasEl.getContext("2d");
31304 switch (this.rotate) {
31307 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31308 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31310 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31315 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31316 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31318 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31319 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);
31323 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31328 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31329 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31331 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31332 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);
31336 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);
31341 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31342 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31344 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31345 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31349 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);
31356 this.previewEl.appendChild(this.canvasEl);
31358 this.setCanvasPosition();
31363 if(!this.canvasLoaded){
31367 var imageCanvas = document.createElement("canvas");
31369 var imageContext = imageCanvas.getContext("2d");
31371 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31372 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31374 var center = imageCanvas.width / 2;
31376 imageContext.translate(center, center);
31378 imageContext.rotate(this.rotate * Math.PI / 180);
31380 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31382 var canvas = document.createElement("canvas");
31384 var context = canvas.getContext("2d");
31386 canvas.width = this.minWidth;
31387 canvas.height = this.minHeight;
31389 switch (this.rotate) {
31392 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31393 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31395 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31396 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31398 var targetWidth = this.minWidth - 2 * x;
31399 var targetHeight = this.minHeight - 2 * y;
31403 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31404 scale = targetWidth / width;
31407 if(x > 0 && y == 0){
31408 scale = targetHeight / height;
31411 if(x > 0 && y > 0){
31412 scale = targetWidth / width;
31414 if(width < height){
31415 scale = targetHeight / height;
31419 context.scale(scale, scale);
31421 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31422 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31424 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31425 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31427 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31432 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31433 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31435 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31436 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31438 var targetWidth = this.minWidth - 2 * x;
31439 var targetHeight = this.minHeight - 2 * y;
31443 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31444 scale = targetWidth / width;
31447 if(x > 0 && y == 0){
31448 scale = targetHeight / height;
31451 if(x > 0 && y > 0){
31452 scale = targetWidth / width;
31454 if(width < height){
31455 scale = targetHeight / height;
31459 context.scale(scale, scale);
31461 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31462 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31464 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31465 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31467 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31469 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31474 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31475 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31477 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31478 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31480 var targetWidth = this.minWidth - 2 * x;
31481 var targetHeight = this.minHeight - 2 * y;
31485 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31486 scale = targetWidth / width;
31489 if(x > 0 && y == 0){
31490 scale = targetHeight / height;
31493 if(x > 0 && y > 0){
31494 scale = targetWidth / width;
31496 if(width < height){
31497 scale = targetHeight / height;
31501 context.scale(scale, scale);
31503 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31504 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31506 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31507 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31509 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31510 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31512 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31517 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31518 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31520 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31521 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31523 var targetWidth = this.minWidth - 2 * x;
31524 var targetHeight = this.minHeight - 2 * y;
31528 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31529 scale = targetWidth / width;
31532 if(x > 0 && y == 0){
31533 scale = targetHeight / height;
31536 if(x > 0 && y > 0){
31537 scale = targetWidth / width;
31539 if(width < height){
31540 scale = targetHeight / height;
31544 context.scale(scale, scale);
31546 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31547 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31549 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31550 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31552 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31554 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31561 this.cropData = canvas.toDataURL(this.cropType);
31563 if(this.fireEvent('crop', this, this.cropData) !== false){
31564 this.process(this.file, this.cropData);
31571 setThumbBoxSize : function()
31575 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31576 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31577 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31579 this.minWidth = width;
31580 this.minHeight = height;
31582 if(this.rotate == 90 || this.rotate == 270){
31583 this.minWidth = height;
31584 this.minHeight = width;
31589 width = Math.ceil(this.minWidth * height / this.minHeight);
31591 if(this.minWidth > this.minHeight){
31593 height = Math.ceil(this.minHeight * width / this.minWidth);
31596 this.thumbEl.setStyle({
31597 width : width + 'px',
31598 height : height + 'px'
31605 setThumbBoxPosition : function()
31607 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31608 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31610 this.thumbEl.setLeft(x);
31611 this.thumbEl.setTop(y);
31615 baseRotateLevel : function()
31617 this.baseRotate = 1;
31620 typeof(this.exif) != 'undefined' &&
31621 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31622 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31624 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31627 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31631 baseScaleLevel : function()
31635 if(this.isDocument){
31637 if(this.baseRotate == 6 || this.baseRotate == 8){
31639 height = this.thumbEl.getHeight();
31640 this.baseScale = height / this.imageEl.OriginWidth;
31642 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31643 width = this.thumbEl.getWidth();
31644 this.baseScale = width / this.imageEl.OriginHeight;
31650 height = this.thumbEl.getHeight();
31651 this.baseScale = height / this.imageEl.OriginHeight;
31653 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31654 width = this.thumbEl.getWidth();
31655 this.baseScale = width / this.imageEl.OriginWidth;
31661 if(this.baseRotate == 6 || this.baseRotate == 8){
31663 width = this.thumbEl.getHeight();
31664 this.baseScale = width / this.imageEl.OriginHeight;
31666 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31667 height = this.thumbEl.getWidth();
31668 this.baseScale = height / this.imageEl.OriginHeight;
31671 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31672 height = this.thumbEl.getWidth();
31673 this.baseScale = height / this.imageEl.OriginHeight;
31675 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31676 width = this.thumbEl.getHeight();
31677 this.baseScale = width / this.imageEl.OriginWidth;
31684 width = this.thumbEl.getWidth();
31685 this.baseScale = width / this.imageEl.OriginWidth;
31687 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31688 height = this.thumbEl.getHeight();
31689 this.baseScale = height / this.imageEl.OriginHeight;
31692 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31694 height = this.thumbEl.getHeight();
31695 this.baseScale = height / this.imageEl.OriginHeight;
31697 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31698 width = this.thumbEl.getWidth();
31699 this.baseScale = width / this.imageEl.OriginWidth;
31707 getScaleLevel : function()
31709 return this.baseScale * Math.pow(1.1, this.scale);
31712 onTouchStart : function(e)
31714 if(!this.canvasLoaded){
31715 this.beforeSelectFile(e);
31719 var touches = e.browserEvent.touches;
31725 if(touches.length == 1){
31726 this.onMouseDown(e);
31730 if(touches.length != 2){
31736 for(var i = 0, finger; finger = touches[i]; i++){
31737 coords.push(finger.pageX, finger.pageY);
31740 var x = Math.pow(coords[0] - coords[2], 2);
31741 var y = Math.pow(coords[1] - coords[3], 2);
31743 this.startDistance = Math.sqrt(x + y);
31745 this.startScale = this.scale;
31747 this.pinching = true;
31748 this.dragable = false;
31752 onTouchMove : function(e)
31754 if(!this.pinching && !this.dragable){
31758 var touches = e.browserEvent.touches;
31765 this.onMouseMove(e);
31771 for(var i = 0, finger; finger = touches[i]; i++){
31772 coords.push(finger.pageX, finger.pageY);
31775 var x = Math.pow(coords[0] - coords[2], 2);
31776 var y = Math.pow(coords[1] - coords[3], 2);
31778 this.endDistance = Math.sqrt(x + y);
31780 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31782 if(!this.zoomable()){
31783 this.scale = this.startScale;
31791 onTouchEnd : function(e)
31793 this.pinching = false;
31794 this.dragable = false;
31798 process : function(file, crop)
31801 this.maskEl.mask(this.loadingText);
31804 this.xhr = new XMLHttpRequest();
31806 file.xhr = this.xhr;
31808 this.xhr.open(this.method, this.url, true);
31811 "Accept": "application/json",
31812 "Cache-Control": "no-cache",
31813 "X-Requested-With": "XMLHttpRequest"
31816 for (var headerName in headers) {
31817 var headerValue = headers[headerName];
31819 this.xhr.setRequestHeader(headerName, headerValue);
31825 this.xhr.onload = function()
31827 _this.xhrOnLoad(_this.xhr);
31830 this.xhr.onerror = function()
31832 _this.xhrOnError(_this.xhr);
31835 var formData = new FormData();
31837 formData.append('returnHTML', 'NO');
31840 formData.append('crop', crop);
31843 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31844 formData.append(this.paramName, file, file.name);
31847 if(typeof(file.filename) != 'undefined'){
31848 formData.append('filename', file.filename);
31851 if(typeof(file.mimetype) != 'undefined'){
31852 formData.append('mimetype', file.mimetype);
31855 if(this.fireEvent('arrange', this, formData) != false){
31856 this.xhr.send(formData);
31860 xhrOnLoad : function(xhr)
31863 this.maskEl.unmask();
31866 if (xhr.readyState !== 4) {
31867 this.fireEvent('exception', this, xhr);
31871 var response = Roo.decode(xhr.responseText);
31873 if(!response.success){
31874 this.fireEvent('exception', this, xhr);
31878 var response = Roo.decode(xhr.responseText);
31880 this.fireEvent('upload', this, response);
31884 xhrOnError : function()
31887 this.maskEl.unmask();
31890 Roo.log('xhr on error');
31892 var response = Roo.decode(xhr.responseText);
31898 prepare : function(file)
31901 this.maskEl.mask(this.loadingText);
31907 if(typeof(file) === 'string'){
31908 this.loadCanvas(file);
31912 if(!file || !this.urlAPI){
31917 this.cropType = file.type;
31921 if(this.fireEvent('prepare', this, this.file) != false){
31923 var reader = new FileReader();
31925 reader.onload = function (e) {
31926 if (e.target.error) {
31927 Roo.log(e.target.error);
31931 var buffer = e.target.result,
31932 dataView = new DataView(buffer),
31934 maxOffset = dataView.byteLength - 4,
31938 if (dataView.getUint16(0) === 0xffd8) {
31939 while (offset < maxOffset) {
31940 markerBytes = dataView.getUint16(offset);
31942 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31943 markerLength = dataView.getUint16(offset + 2) + 2;
31944 if (offset + markerLength > dataView.byteLength) {
31945 Roo.log('Invalid meta data: Invalid segment size.');
31949 if(markerBytes == 0xffe1){
31950 _this.parseExifData(
31957 offset += markerLength;
31967 var url = _this.urlAPI.createObjectURL(_this.file);
31969 _this.loadCanvas(url);
31974 reader.readAsArrayBuffer(this.file);
31980 parseExifData : function(dataView, offset, length)
31982 var tiffOffset = offset + 10,
31986 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31987 // No Exif data, might be XMP data instead
31991 // Check for the ASCII code for "Exif" (0x45786966):
31992 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31993 // No Exif data, might be XMP data instead
31996 if (tiffOffset + 8 > dataView.byteLength) {
31997 Roo.log('Invalid Exif data: Invalid segment size.');
32000 // Check for the two null bytes:
32001 if (dataView.getUint16(offset + 8) !== 0x0000) {
32002 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32005 // Check the byte alignment:
32006 switch (dataView.getUint16(tiffOffset)) {
32008 littleEndian = true;
32011 littleEndian = false;
32014 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32017 // Check for the TIFF tag marker (0x002A):
32018 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32019 Roo.log('Invalid Exif data: Missing TIFF marker.');
32022 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32023 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32025 this.parseExifTags(
32028 tiffOffset + dirOffset,
32033 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32038 if (dirOffset + 6 > dataView.byteLength) {
32039 Roo.log('Invalid Exif data: Invalid directory offset.');
32042 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32043 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32044 if (dirEndOffset + 4 > dataView.byteLength) {
32045 Roo.log('Invalid Exif data: Invalid directory size.');
32048 for (i = 0; i < tagsNumber; i += 1) {
32052 dirOffset + 2 + 12 * i, // tag offset
32056 // Return the offset to the next directory:
32057 return dataView.getUint32(dirEndOffset, littleEndian);
32060 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32062 var tag = dataView.getUint16(offset, littleEndian);
32064 this.exif[tag] = this.getExifValue(
32068 dataView.getUint16(offset + 2, littleEndian), // tag type
32069 dataView.getUint32(offset + 4, littleEndian), // tag length
32074 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32076 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32085 Roo.log('Invalid Exif data: Invalid tag type.');
32089 tagSize = tagType.size * length;
32090 // Determine if the value is contained in the dataOffset bytes,
32091 // or if the value at the dataOffset is a pointer to the actual data:
32092 dataOffset = tagSize > 4 ?
32093 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32094 if (dataOffset + tagSize > dataView.byteLength) {
32095 Roo.log('Invalid Exif data: Invalid data offset.');
32098 if (length === 1) {
32099 return tagType.getValue(dataView, dataOffset, littleEndian);
32102 for (i = 0; i < length; i += 1) {
32103 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32106 if (tagType.ascii) {
32108 // Concatenate the chars:
32109 for (i = 0; i < values.length; i += 1) {
32111 // Ignore the terminating NULL byte(s):
32112 if (c === '\u0000') {
32124 Roo.apply(Roo.bootstrap.UploadCropbox, {
32126 'Orientation': 0x0112
32130 1: 0, //'top-left',
32132 3: 180, //'bottom-right',
32133 // 4: 'bottom-left',
32135 6: 90, //'right-top',
32136 // 7: 'right-bottom',
32137 8: 270 //'left-bottom'
32141 // byte, 8-bit unsigned int:
32143 getValue: function (dataView, dataOffset) {
32144 return dataView.getUint8(dataOffset);
32148 // ascii, 8-bit byte:
32150 getValue: function (dataView, dataOffset) {
32151 return String.fromCharCode(dataView.getUint8(dataOffset));
32156 // short, 16 bit int:
32158 getValue: function (dataView, dataOffset, littleEndian) {
32159 return dataView.getUint16(dataOffset, littleEndian);
32163 // long, 32 bit int:
32165 getValue: function (dataView, dataOffset, littleEndian) {
32166 return dataView.getUint32(dataOffset, littleEndian);
32170 // rational = two long values, first is numerator, second is denominator:
32172 getValue: function (dataView, dataOffset, littleEndian) {
32173 return dataView.getUint32(dataOffset, littleEndian) /
32174 dataView.getUint32(dataOffset + 4, littleEndian);
32178 // slong, 32 bit signed int:
32180 getValue: function (dataView, dataOffset, littleEndian) {
32181 return dataView.getInt32(dataOffset, littleEndian);
32185 // srational, two slongs, first is numerator, second is denominator:
32187 getValue: function (dataView, dataOffset, littleEndian) {
32188 return dataView.getInt32(dataOffset, littleEndian) /
32189 dataView.getInt32(dataOffset + 4, littleEndian);
32199 cls : 'btn-group roo-upload-cropbox-rotate-left',
32200 action : 'rotate-left',
32204 cls : 'btn btn-default',
32205 html : '<i class="fa fa-undo"></i>'
32211 cls : 'btn-group roo-upload-cropbox-picture',
32212 action : 'picture',
32216 cls : 'btn btn-default',
32217 html : '<i class="fa fa-picture-o"></i>'
32223 cls : 'btn-group roo-upload-cropbox-rotate-right',
32224 action : 'rotate-right',
32228 cls : 'btn btn-default',
32229 html : '<i class="fa fa-repeat"></i>'
32237 cls : 'btn-group roo-upload-cropbox-rotate-left',
32238 action : 'rotate-left',
32242 cls : 'btn btn-default',
32243 html : '<i class="fa fa-undo"></i>'
32249 cls : 'btn-group roo-upload-cropbox-download',
32250 action : 'download',
32254 cls : 'btn btn-default',
32255 html : '<i class="fa fa-download"></i>'
32261 cls : 'btn-group roo-upload-cropbox-crop',
32266 cls : 'btn btn-default',
32267 html : '<i class="fa fa-crop"></i>'
32273 cls : 'btn-group roo-upload-cropbox-trash',
32278 cls : 'btn btn-default',
32279 html : '<i class="fa fa-trash"></i>'
32285 cls : 'btn-group roo-upload-cropbox-rotate-right',
32286 action : 'rotate-right',
32290 cls : 'btn btn-default',
32291 html : '<i class="fa fa-repeat"></i>'
32299 cls : 'btn-group roo-upload-cropbox-rotate-left',
32300 action : 'rotate-left',
32304 cls : 'btn btn-default',
32305 html : '<i class="fa fa-undo"></i>'
32311 cls : 'btn-group roo-upload-cropbox-rotate-right',
32312 action : 'rotate-right',
32316 cls : 'btn btn-default',
32317 html : '<i class="fa fa-repeat"></i>'
32330 * @class Roo.bootstrap.DocumentManager
32331 * @extends Roo.bootstrap.Component
32332 * Bootstrap DocumentManager class
32333 * @cfg {String} paramName default 'imageUpload'
32334 * @cfg {String} toolTipName default 'filename'
32335 * @cfg {String} method default POST
32336 * @cfg {String} url action url
32337 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32338 * @cfg {Boolean} multiple multiple upload default true
32339 * @cfg {Number} thumbSize default 300
32340 * @cfg {String} fieldLabel
32341 * @cfg {Number} labelWidth default 4
32342 * @cfg {String} labelAlign (left|top) default left
32343 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32344 * @cfg {Number} labellg set the width of label (1-12)
32345 * @cfg {Number} labelmd set the width of label (1-12)
32346 * @cfg {Number} labelsm set the width of label (1-12)
32347 * @cfg {Number} labelxs set the width of label (1-12)
32350 * Create a new DocumentManager
32351 * @param {Object} config The config object
32354 Roo.bootstrap.DocumentManager = function(config){
32355 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32358 this.delegates = [];
32363 * Fire when initial the DocumentManager
32364 * @param {Roo.bootstrap.DocumentManager} this
32369 * inspect selected file
32370 * @param {Roo.bootstrap.DocumentManager} this
32371 * @param {File} file
32376 * Fire when xhr load exception
32377 * @param {Roo.bootstrap.DocumentManager} this
32378 * @param {XMLHttpRequest} xhr
32380 "exception" : true,
32382 * @event afterupload
32383 * Fire when xhr load exception
32384 * @param {Roo.bootstrap.DocumentManager} this
32385 * @param {XMLHttpRequest} xhr
32387 "afterupload" : true,
32390 * prepare the form data
32391 * @param {Roo.bootstrap.DocumentManager} this
32392 * @param {Object} formData
32397 * Fire when remove the file
32398 * @param {Roo.bootstrap.DocumentManager} this
32399 * @param {Object} file
32404 * Fire after refresh the file
32405 * @param {Roo.bootstrap.DocumentManager} this
32410 * Fire after click the image
32411 * @param {Roo.bootstrap.DocumentManager} this
32412 * @param {Object} file
32417 * Fire when upload a image and editable set to true
32418 * @param {Roo.bootstrap.DocumentManager} this
32419 * @param {Object} file
32423 * @event beforeselectfile
32424 * Fire before select file
32425 * @param {Roo.bootstrap.DocumentManager} this
32427 "beforeselectfile" : true,
32430 * Fire before process file
32431 * @param {Roo.bootstrap.DocumentManager} this
32432 * @param {Object} file
32436 * @event previewrendered
32437 * Fire when preview rendered
32438 * @param {Roo.bootstrap.DocumentManager} this
32439 * @param {Object} file
32441 "previewrendered" : true,
32444 "previewResize" : true
32449 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32458 paramName : 'imageUpload',
32459 toolTipName : 'filename',
32462 labelAlign : 'left',
32472 getAutoCreate : function()
32474 var managerWidget = {
32476 cls : 'roo-document-manager',
32480 cls : 'roo-document-manager-selector',
32485 cls : 'roo-document-manager-uploader',
32489 cls : 'roo-document-manager-upload-btn',
32490 html : '<i class="fa fa-plus"></i>'
32501 cls : 'column col-md-12',
32506 if(this.fieldLabel.length){
32511 cls : 'column col-md-12',
32512 html : this.fieldLabel
32516 cls : 'column col-md-12',
32521 if(this.labelAlign == 'left'){
32526 html : this.fieldLabel
32535 if(this.labelWidth > 12){
32536 content[0].style = "width: " + this.labelWidth + 'px';
32539 if(this.labelWidth < 13 && this.labelmd == 0){
32540 this.labelmd = this.labelWidth;
32543 if(this.labellg > 0){
32544 content[0].cls += ' col-lg-' + this.labellg;
32545 content[1].cls += ' col-lg-' + (12 - this.labellg);
32548 if(this.labelmd > 0){
32549 content[0].cls += ' col-md-' + this.labelmd;
32550 content[1].cls += ' col-md-' + (12 - this.labelmd);
32553 if(this.labelsm > 0){
32554 content[0].cls += ' col-sm-' + this.labelsm;
32555 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32558 if(this.labelxs > 0){
32559 content[0].cls += ' col-xs-' + this.labelxs;
32560 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32568 cls : 'row clearfix',
32576 initEvents : function()
32578 this.managerEl = this.el.select('.roo-document-manager', true).first();
32579 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32581 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32582 this.selectorEl.hide();
32585 this.selectorEl.attr('multiple', 'multiple');
32588 this.selectorEl.on('change', this.onFileSelected, this);
32590 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32591 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32593 this.uploader.on('click', this.onUploaderClick, this);
32595 this.renderProgressDialog();
32599 window.addEventListener("resize", function() { _this.refresh(); } );
32601 this.fireEvent('initial', this);
32604 renderProgressDialog : function()
32608 this.progressDialog = new Roo.bootstrap.Modal({
32609 cls : 'roo-document-manager-progress-dialog',
32610 allow_close : false,
32621 btnclick : function() {
32622 _this.uploadCancel();
32628 this.progressDialog.render(Roo.get(document.body));
32630 this.progress = new Roo.bootstrap.Progress({
32631 cls : 'roo-document-manager-progress',
32636 this.progress.render(this.progressDialog.getChildContainer());
32638 this.progressBar = new Roo.bootstrap.ProgressBar({
32639 cls : 'roo-document-manager-progress-bar',
32642 aria_valuemax : 12,
32646 this.progressBar.render(this.progress.getChildContainer());
32649 onUploaderClick : function(e)
32651 e.preventDefault();
32653 if(this.fireEvent('beforeselectfile', this) != false){
32654 this.selectorEl.dom.click();
32659 onFileSelected : function(e)
32661 e.preventDefault();
32663 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32667 Roo.each(this.selectorEl.dom.files, function(file){
32668 if(this.fireEvent('inspect', this, file) != false){
32669 this.files.push(file);
32679 this.selectorEl.dom.value = '';
32681 if(!this.files || !this.files.length){
32685 if(this.boxes > 0 && this.files.length > this.boxes){
32686 this.files = this.files.slice(0, this.boxes);
32689 this.uploader.show();
32691 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32692 this.uploader.hide();
32701 Roo.each(this.files, function(file){
32703 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32704 var f = this.renderPreview(file);
32709 if(file.type.indexOf('image') != -1){
32710 this.delegates.push(
32712 _this.process(file);
32713 }).createDelegate(this)
32721 _this.process(file);
32722 }).createDelegate(this)
32727 this.files = files;
32729 this.delegates = this.delegates.concat(docs);
32731 if(!this.delegates.length){
32736 this.progressBar.aria_valuemax = this.delegates.length;
32743 arrange : function()
32745 if(!this.delegates.length){
32746 this.progressDialog.hide();
32751 var delegate = this.delegates.shift();
32753 this.progressDialog.show();
32755 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32757 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32762 refresh : function()
32764 this.uploader.show();
32766 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32767 this.uploader.hide();
32770 Roo.isTouch ? this.closable(false) : this.closable(true);
32772 this.fireEvent('refresh', this);
32775 onRemove : function(e, el, o)
32777 e.preventDefault();
32779 this.fireEvent('remove', this, o);
32783 remove : function(o)
32787 Roo.each(this.files, function(file){
32788 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32797 this.files = files;
32804 Roo.each(this.files, function(file){
32809 file.target.remove();
32818 onClick : function(e, el, o)
32820 e.preventDefault();
32822 this.fireEvent('click', this, o);
32826 closable : function(closable)
32828 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32830 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32842 xhrOnLoad : function(xhr)
32844 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32848 if (xhr.readyState !== 4) {
32850 this.fireEvent('exception', this, xhr);
32854 var response = Roo.decode(xhr.responseText);
32856 if(!response.success){
32858 this.fireEvent('exception', this, xhr);
32862 var file = this.renderPreview(response.data);
32864 this.files.push(file);
32868 this.fireEvent('afterupload', this, xhr);
32872 xhrOnError : function(xhr)
32874 Roo.log('xhr on error');
32876 var response = Roo.decode(xhr.responseText);
32883 process : function(file)
32885 if(this.fireEvent('process', this, file) !== false){
32886 if(this.editable && file.type.indexOf('image') != -1){
32887 this.fireEvent('edit', this, file);
32891 this.uploadStart(file, false);
32898 uploadStart : function(file, crop)
32900 this.xhr = new XMLHttpRequest();
32902 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32907 file.xhr = this.xhr;
32909 this.managerEl.createChild({
32911 cls : 'roo-document-manager-loading',
32915 tooltip : file.name,
32916 cls : 'roo-document-manager-thumb',
32917 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32923 this.xhr.open(this.method, this.url, true);
32926 "Accept": "application/json",
32927 "Cache-Control": "no-cache",
32928 "X-Requested-With": "XMLHttpRequest"
32931 for (var headerName in headers) {
32932 var headerValue = headers[headerName];
32934 this.xhr.setRequestHeader(headerName, headerValue);
32940 this.xhr.onload = function()
32942 _this.xhrOnLoad(_this.xhr);
32945 this.xhr.onerror = function()
32947 _this.xhrOnError(_this.xhr);
32950 var formData = new FormData();
32952 formData.append('returnHTML', 'NO');
32955 formData.append('crop', crop);
32958 formData.append(this.paramName, file, file.name);
32965 if(this.fireEvent('prepare', this, formData, options) != false){
32967 if(options.manually){
32971 this.xhr.send(formData);
32975 this.uploadCancel();
32978 uploadCancel : function()
32984 this.delegates = [];
32986 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32993 renderPreview : function(file)
32995 if(typeof(file.target) != 'undefined' && file.target){
32999 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33001 var previewEl = this.managerEl.createChild({
33003 cls : 'roo-document-manager-preview',
33007 tooltip : file[this.toolTipName],
33008 cls : 'roo-document-manager-thumb',
33009 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33014 html : '<i class="fa fa-times-circle"></i>'
33019 var close = previewEl.select('button.close', true).first();
33021 close.on('click', this.onRemove, this, file);
33023 file.target = previewEl;
33025 var image = previewEl.select('img', true).first();
33029 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33031 image.on('click', this.onClick, this, file);
33033 this.fireEvent('previewrendered', this, file);
33039 onPreviewLoad : function(file, image)
33041 if(typeof(file.target) == 'undefined' || !file.target){
33045 var width = image.dom.naturalWidth || image.dom.width;
33046 var height = image.dom.naturalHeight || image.dom.height;
33048 if(!this.previewResize) {
33052 if(width > height){
33053 file.target.addClass('wide');
33057 file.target.addClass('tall');
33062 uploadFromSource : function(file, crop)
33064 this.xhr = new XMLHttpRequest();
33066 this.managerEl.createChild({
33068 cls : 'roo-document-manager-loading',
33072 tooltip : file.name,
33073 cls : 'roo-document-manager-thumb',
33074 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33080 this.xhr.open(this.method, this.url, true);
33083 "Accept": "application/json",
33084 "Cache-Control": "no-cache",
33085 "X-Requested-With": "XMLHttpRequest"
33088 for (var headerName in headers) {
33089 var headerValue = headers[headerName];
33091 this.xhr.setRequestHeader(headerName, headerValue);
33097 this.xhr.onload = function()
33099 _this.xhrOnLoad(_this.xhr);
33102 this.xhr.onerror = function()
33104 _this.xhrOnError(_this.xhr);
33107 var formData = new FormData();
33109 formData.append('returnHTML', 'NO');
33111 formData.append('crop', crop);
33113 if(typeof(file.filename) != 'undefined'){
33114 formData.append('filename', file.filename);
33117 if(typeof(file.mimetype) != 'undefined'){
33118 formData.append('mimetype', file.mimetype);
33123 if(this.fireEvent('prepare', this, formData) != false){
33124 this.xhr.send(formData);
33134 * @class Roo.bootstrap.DocumentViewer
33135 * @extends Roo.bootstrap.Component
33136 * Bootstrap DocumentViewer class
33137 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33138 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33141 * Create a new DocumentViewer
33142 * @param {Object} config The config object
33145 Roo.bootstrap.DocumentViewer = function(config){
33146 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33151 * Fire after initEvent
33152 * @param {Roo.bootstrap.DocumentViewer} this
33158 * @param {Roo.bootstrap.DocumentViewer} this
33163 * Fire after download button
33164 * @param {Roo.bootstrap.DocumentViewer} this
33169 * Fire after trash button
33170 * @param {Roo.bootstrap.DocumentViewer} this
33177 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33179 showDownload : true,
33183 getAutoCreate : function()
33187 cls : 'roo-document-viewer',
33191 cls : 'roo-document-viewer-body',
33195 cls : 'roo-document-viewer-thumb',
33199 cls : 'roo-document-viewer-image'
33207 cls : 'roo-document-viewer-footer',
33210 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33214 cls : 'btn-group roo-document-viewer-download',
33218 cls : 'btn btn-default',
33219 html : '<i class="fa fa-download"></i>'
33225 cls : 'btn-group roo-document-viewer-trash',
33229 cls : 'btn btn-default',
33230 html : '<i class="fa fa-trash"></i>'
33243 initEvents : function()
33245 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33246 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33248 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33249 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33251 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33252 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33254 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33255 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33257 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33258 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33260 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33261 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33263 this.bodyEl.on('click', this.onClick, this);
33264 this.downloadBtn.on('click', this.onDownload, this);
33265 this.trashBtn.on('click', this.onTrash, this);
33267 this.downloadBtn.hide();
33268 this.trashBtn.hide();
33270 if(this.showDownload){
33271 this.downloadBtn.show();
33274 if(this.showTrash){
33275 this.trashBtn.show();
33278 if(!this.showDownload && !this.showTrash) {
33279 this.footerEl.hide();
33284 initial : function()
33286 this.fireEvent('initial', this);
33290 onClick : function(e)
33292 e.preventDefault();
33294 this.fireEvent('click', this);
33297 onDownload : function(e)
33299 e.preventDefault();
33301 this.fireEvent('download', this);
33304 onTrash : function(e)
33306 e.preventDefault();
33308 this.fireEvent('trash', this);
33320 * @class Roo.bootstrap.NavProgressBar
33321 * @extends Roo.bootstrap.Component
33322 * Bootstrap NavProgressBar class
33325 * Create a new nav progress bar
33326 * @param {Object} config The config object
33329 Roo.bootstrap.NavProgressBar = function(config){
33330 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33332 this.bullets = this.bullets || [];
33334 // Roo.bootstrap.NavProgressBar.register(this);
33338 * Fires when the active item changes
33339 * @param {Roo.bootstrap.NavProgressBar} this
33340 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33341 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33348 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33353 getAutoCreate : function()
33355 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33359 cls : 'roo-navigation-bar-group',
33363 cls : 'roo-navigation-top-bar'
33367 cls : 'roo-navigation-bullets-bar',
33371 cls : 'roo-navigation-bar'
33378 cls : 'roo-navigation-bottom-bar'
33388 initEvents: function()
33393 onRender : function(ct, position)
33395 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33397 if(this.bullets.length){
33398 Roo.each(this.bullets, function(b){
33407 addItem : function(cfg)
33409 var item = new Roo.bootstrap.NavProgressItem(cfg);
33411 item.parentId = this.id;
33412 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33415 var top = new Roo.bootstrap.Element({
33417 cls : 'roo-navigation-bar-text'
33420 var bottom = new Roo.bootstrap.Element({
33422 cls : 'roo-navigation-bar-text'
33425 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33426 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33428 var topText = new Roo.bootstrap.Element({
33430 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33433 var bottomText = new Roo.bootstrap.Element({
33435 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33438 topText.onRender(top.el, null);
33439 bottomText.onRender(bottom.el, null);
33442 item.bottomEl = bottom;
33445 this.barItems.push(item);
33450 getActive : function()
33452 var active = false;
33454 Roo.each(this.barItems, function(v){
33456 if (!v.isActive()) {
33468 setActiveItem : function(item)
33472 Roo.each(this.barItems, function(v){
33473 if (v.rid == item.rid) {
33477 if (v.isActive()) {
33478 v.setActive(false);
33483 item.setActive(true);
33485 this.fireEvent('changed', this, item, prev);
33488 getBarItem: function(rid)
33492 Roo.each(this.barItems, function(e) {
33493 if (e.rid != rid) {
33504 indexOfItem : function(item)
33508 Roo.each(this.barItems, function(v, i){
33510 if (v.rid != item.rid) {
33521 setActiveNext : function()
33523 var i = this.indexOfItem(this.getActive());
33525 if (i > this.barItems.length) {
33529 this.setActiveItem(this.barItems[i+1]);
33532 setActivePrev : function()
33534 var i = this.indexOfItem(this.getActive());
33540 this.setActiveItem(this.barItems[i-1]);
33543 format : function()
33545 if(!this.barItems.length){
33549 var width = 100 / this.barItems.length;
33551 Roo.each(this.barItems, function(i){
33552 i.el.setStyle('width', width + '%');
33553 i.topEl.el.setStyle('width', width + '%');
33554 i.bottomEl.el.setStyle('width', width + '%');
33563 * Nav Progress Item
33568 * @class Roo.bootstrap.NavProgressItem
33569 * @extends Roo.bootstrap.Component
33570 * Bootstrap NavProgressItem class
33571 * @cfg {String} rid the reference id
33572 * @cfg {Boolean} active (true|false) Is item active default false
33573 * @cfg {Boolean} disabled (true|false) Is item active default false
33574 * @cfg {String} html
33575 * @cfg {String} position (top|bottom) text position default bottom
33576 * @cfg {String} icon show icon instead of number
33579 * Create a new NavProgressItem
33580 * @param {Object} config The config object
33582 Roo.bootstrap.NavProgressItem = function(config){
33583 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33588 * The raw click event for the entire grid.
33589 * @param {Roo.bootstrap.NavProgressItem} this
33590 * @param {Roo.EventObject} e
33597 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33603 position : 'bottom',
33606 getAutoCreate : function()
33608 var iconCls = 'roo-navigation-bar-item-icon';
33610 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33614 cls: 'roo-navigation-bar-item',
33624 cfg.cls += ' active';
33627 cfg.cls += ' disabled';
33633 disable : function()
33635 this.setDisabled(true);
33638 enable : function()
33640 this.setDisabled(false);
33643 initEvents: function()
33645 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33647 this.iconEl.on('click', this.onClick, this);
33650 onClick : function(e)
33652 e.preventDefault();
33658 if(this.fireEvent('click', this, e) === false){
33662 this.parent().setActiveItem(this);
33665 isActive: function ()
33667 return this.active;
33670 setActive : function(state)
33672 if(this.active == state){
33676 this.active = state;
33679 this.el.addClass('active');
33683 this.el.removeClass('active');
33688 setDisabled : function(state)
33690 if(this.disabled == state){
33694 this.disabled = state;
33697 this.el.addClass('disabled');
33701 this.el.removeClass('disabled');
33704 tooltipEl : function()
33706 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33719 * @class Roo.bootstrap.FieldLabel
33720 * @extends Roo.bootstrap.Component
33721 * Bootstrap FieldLabel class
33722 * @cfg {String} html contents of the element
33723 * @cfg {String} tag tag of the element default label
33724 * @cfg {String} cls class of the element
33725 * @cfg {String} target label target
33726 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33727 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33728 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33729 * @cfg {String} iconTooltip default "This field is required"
33730 * @cfg {String} indicatorpos (left|right) default left
33733 * Create a new FieldLabel
33734 * @param {Object} config The config object
33737 Roo.bootstrap.FieldLabel = function(config){
33738 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33743 * Fires after the field has been marked as invalid.
33744 * @param {Roo.form.FieldLabel} this
33745 * @param {String} msg The validation message
33750 * Fires after the field has been validated with no errors.
33751 * @param {Roo.form.FieldLabel} this
33757 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33764 invalidClass : 'has-warning',
33765 validClass : 'has-success',
33766 iconTooltip : 'This field is required',
33767 indicatorpos : 'left',
33769 getAutoCreate : function(){
33772 if (!this.allowBlank) {
33778 cls : 'roo-bootstrap-field-label ' + this.cls,
33783 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33784 tooltip : this.iconTooltip
33793 if(this.indicatorpos == 'right'){
33796 cls : 'roo-bootstrap-field-label ' + this.cls,
33805 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33806 tooltip : this.iconTooltip
33815 initEvents: function()
33817 Roo.bootstrap.Element.superclass.initEvents.call(this);
33819 this.indicator = this.indicatorEl();
33821 if(this.indicator){
33822 this.indicator.removeClass('visible');
33823 this.indicator.addClass('invisible');
33826 Roo.bootstrap.FieldLabel.register(this);
33829 indicatorEl : function()
33831 var indicator = this.el.select('i.roo-required-indicator',true).first();
33842 * Mark this field as valid
33844 markValid : function()
33846 if(this.indicator){
33847 this.indicator.removeClass('visible');
33848 this.indicator.addClass('invisible');
33850 if (Roo.bootstrap.version == 3) {
33851 this.el.removeClass(this.invalidClass);
33852 this.el.addClass(this.validClass);
33854 this.el.removeClass('is-invalid');
33855 this.el.addClass('is-valid');
33859 this.fireEvent('valid', this);
33863 * Mark this field as invalid
33864 * @param {String} msg The validation message
33866 markInvalid : function(msg)
33868 if(this.indicator){
33869 this.indicator.removeClass('invisible');
33870 this.indicator.addClass('visible');
33872 if (Roo.bootstrap.version == 3) {
33873 this.el.removeClass(this.validClass);
33874 this.el.addClass(this.invalidClass);
33876 this.el.removeClass('is-valid');
33877 this.el.addClass('is-invalid');
33881 this.fireEvent('invalid', this, msg);
33887 Roo.apply(Roo.bootstrap.FieldLabel, {
33892 * register a FieldLabel Group
33893 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33895 register : function(label)
33897 if(this.groups.hasOwnProperty(label.target)){
33901 this.groups[label.target] = label;
33905 * fetch a FieldLabel Group based on the target
33906 * @param {string} target
33907 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33909 get: function(target) {
33910 if (typeof(this.groups[target]) == 'undefined') {
33914 return this.groups[target] ;
33923 * page DateSplitField.
33929 * @class Roo.bootstrap.DateSplitField
33930 * @extends Roo.bootstrap.Component
33931 * Bootstrap DateSplitField class
33932 * @cfg {string} fieldLabel - the label associated
33933 * @cfg {Number} labelWidth set the width of label (0-12)
33934 * @cfg {String} labelAlign (top|left)
33935 * @cfg {Boolean} dayAllowBlank (true|false) default false
33936 * @cfg {Boolean} monthAllowBlank (true|false) default false
33937 * @cfg {Boolean} yearAllowBlank (true|false) default false
33938 * @cfg {string} dayPlaceholder
33939 * @cfg {string} monthPlaceholder
33940 * @cfg {string} yearPlaceholder
33941 * @cfg {string} dayFormat default 'd'
33942 * @cfg {string} monthFormat default 'm'
33943 * @cfg {string} yearFormat default 'Y'
33944 * @cfg {Number} labellg set the width of label (1-12)
33945 * @cfg {Number} labelmd set the width of label (1-12)
33946 * @cfg {Number} labelsm set the width of label (1-12)
33947 * @cfg {Number} labelxs set the width of label (1-12)
33951 * Create a new DateSplitField
33952 * @param {Object} config The config object
33955 Roo.bootstrap.DateSplitField = function(config){
33956 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33962 * getting the data of years
33963 * @param {Roo.bootstrap.DateSplitField} this
33964 * @param {Object} years
33969 * getting the data of days
33970 * @param {Roo.bootstrap.DateSplitField} this
33971 * @param {Object} days
33976 * Fires after the field has been marked as invalid.
33977 * @param {Roo.form.Field} this
33978 * @param {String} msg The validation message
33983 * Fires after the field has been validated with no errors.
33984 * @param {Roo.form.Field} this
33990 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33993 labelAlign : 'top',
33995 dayAllowBlank : false,
33996 monthAllowBlank : false,
33997 yearAllowBlank : false,
33998 dayPlaceholder : '',
33999 monthPlaceholder : '',
34000 yearPlaceholder : '',
34004 isFormField : true,
34010 getAutoCreate : function()
34014 cls : 'row roo-date-split-field-group',
34019 cls : 'form-hidden-field roo-date-split-field-group-value',
34025 var labelCls = 'col-md-12';
34026 var contentCls = 'col-md-4';
34028 if(this.fieldLabel){
34032 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34036 html : this.fieldLabel
34041 if(this.labelAlign == 'left'){
34043 if(this.labelWidth > 12){
34044 label.style = "width: " + this.labelWidth + 'px';
34047 if(this.labelWidth < 13 && this.labelmd == 0){
34048 this.labelmd = this.labelWidth;
34051 if(this.labellg > 0){
34052 labelCls = ' col-lg-' + this.labellg;
34053 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34056 if(this.labelmd > 0){
34057 labelCls = ' col-md-' + this.labelmd;
34058 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34061 if(this.labelsm > 0){
34062 labelCls = ' col-sm-' + this.labelsm;
34063 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34066 if(this.labelxs > 0){
34067 labelCls = ' col-xs-' + this.labelxs;
34068 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34072 label.cls += ' ' + labelCls;
34074 cfg.cn.push(label);
34077 Roo.each(['day', 'month', 'year'], function(t){
34080 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34087 inputEl: function ()
34089 return this.el.select('.roo-date-split-field-group-value', true).first();
34092 onRender : function(ct, position)
34096 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34098 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34100 this.dayField = new Roo.bootstrap.ComboBox({
34101 allowBlank : this.dayAllowBlank,
34102 alwaysQuery : true,
34103 displayField : 'value',
34106 forceSelection : true,
34108 placeholder : this.dayPlaceholder,
34109 selectOnFocus : true,
34110 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34111 triggerAction : 'all',
34113 valueField : 'value',
34114 store : new Roo.data.SimpleStore({
34115 data : (function() {
34117 _this.fireEvent('days', _this, days);
34120 fields : [ 'value' ]
34123 select : function (_self, record, index)
34125 _this.setValue(_this.getValue());
34130 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34132 this.monthField = new Roo.bootstrap.MonthField({
34133 after : '<i class=\"fa fa-calendar\"></i>',
34134 allowBlank : this.monthAllowBlank,
34135 placeholder : this.monthPlaceholder,
34138 render : function (_self)
34140 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34141 e.preventDefault();
34145 select : function (_self, oldvalue, newvalue)
34147 _this.setValue(_this.getValue());
34152 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34154 this.yearField = new Roo.bootstrap.ComboBox({
34155 allowBlank : this.yearAllowBlank,
34156 alwaysQuery : true,
34157 displayField : 'value',
34160 forceSelection : true,
34162 placeholder : this.yearPlaceholder,
34163 selectOnFocus : true,
34164 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34165 triggerAction : 'all',
34167 valueField : 'value',
34168 store : new Roo.data.SimpleStore({
34169 data : (function() {
34171 _this.fireEvent('years', _this, years);
34174 fields : [ 'value' ]
34177 select : function (_self, record, index)
34179 _this.setValue(_this.getValue());
34184 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34187 setValue : function(v, format)
34189 this.inputEl.dom.value = v;
34191 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34193 var d = Date.parseDate(v, f);
34200 this.setDay(d.format(this.dayFormat));
34201 this.setMonth(d.format(this.monthFormat));
34202 this.setYear(d.format(this.yearFormat));
34209 setDay : function(v)
34211 this.dayField.setValue(v);
34212 this.inputEl.dom.value = this.getValue();
34217 setMonth : function(v)
34219 this.monthField.setValue(v, true);
34220 this.inputEl.dom.value = this.getValue();
34225 setYear : function(v)
34227 this.yearField.setValue(v);
34228 this.inputEl.dom.value = this.getValue();
34233 getDay : function()
34235 return this.dayField.getValue();
34238 getMonth : function()
34240 return this.monthField.getValue();
34243 getYear : function()
34245 return this.yearField.getValue();
34248 getValue : function()
34250 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34252 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34262 this.inputEl.dom.value = '';
34267 validate : function()
34269 var d = this.dayField.validate();
34270 var m = this.monthField.validate();
34271 var y = this.yearField.validate();
34276 (!this.dayAllowBlank && !d) ||
34277 (!this.monthAllowBlank && !m) ||
34278 (!this.yearAllowBlank && !y)
34283 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34292 this.markInvalid();
34297 markValid : function()
34300 var label = this.el.select('label', true).first();
34301 var icon = this.el.select('i.fa-star', true).first();
34307 this.fireEvent('valid', this);
34311 * Mark this field as invalid
34312 * @param {String} msg The validation message
34314 markInvalid : function(msg)
34317 var label = this.el.select('label', true).first();
34318 var icon = this.el.select('i.fa-star', true).first();
34320 if(label && !icon){
34321 this.el.select('.roo-date-split-field-label', true).createChild({
34323 cls : 'text-danger fa fa-lg fa-star',
34324 tooltip : 'This field is required',
34325 style : 'margin-right:5px;'
34329 this.fireEvent('invalid', this, msg);
34332 clearInvalid : function()
34334 var label = this.el.select('label', true).first();
34335 var icon = this.el.select('i.fa-star', true).first();
34341 this.fireEvent('valid', this);
34344 getName: function()
34354 * http://masonry.desandro.com
34356 * The idea is to render all the bricks based on vertical width...
34358 * The original code extends 'outlayer' - we might need to use that....
34364 * @class Roo.bootstrap.LayoutMasonry
34365 * @extends Roo.bootstrap.Component
34366 * Bootstrap Layout Masonry class
34369 * Create a new Element
34370 * @param {Object} config The config object
34373 Roo.bootstrap.LayoutMasonry = function(config){
34375 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34379 Roo.bootstrap.LayoutMasonry.register(this);
34385 * Fire after layout the items
34386 * @param {Roo.bootstrap.LayoutMasonry} this
34387 * @param {Roo.EventObject} e
34394 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34397 * @cfg {Boolean} isLayoutInstant = no animation?
34399 isLayoutInstant : false, // needed?
34402 * @cfg {Number} boxWidth width of the columns
34407 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34412 * @cfg {Number} padWidth padding below box..
34417 * @cfg {Number} gutter gutter width..
34422 * @cfg {Number} maxCols maximum number of columns
34428 * @cfg {Boolean} isAutoInitial defalut true
34430 isAutoInitial : true,
34435 * @cfg {Boolean} isHorizontal defalut false
34437 isHorizontal : false,
34439 currentSize : null,
34445 bricks: null, //CompositeElement
34449 _isLayoutInited : false,
34451 // isAlternative : false, // only use for vertical layout...
34454 * @cfg {Number} alternativePadWidth padding below box..
34456 alternativePadWidth : 50,
34458 selectedBrick : [],
34460 getAutoCreate : function(){
34462 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34466 cls: 'blog-masonary-wrapper ' + this.cls,
34468 cls : 'mas-boxes masonary'
34475 getChildContainer: function( )
34477 if (this.boxesEl) {
34478 return this.boxesEl;
34481 this.boxesEl = this.el.select('.mas-boxes').first();
34483 return this.boxesEl;
34487 initEvents : function()
34491 if(this.isAutoInitial){
34492 Roo.log('hook children rendered');
34493 this.on('childrenrendered', function() {
34494 Roo.log('children rendered');
34500 initial : function()
34502 this.selectedBrick = [];
34504 this.currentSize = this.el.getBox(true);
34506 Roo.EventManager.onWindowResize(this.resize, this);
34508 if(!this.isAutoInitial){
34516 //this.layout.defer(500,this);
34520 resize : function()
34522 var cs = this.el.getBox(true);
34525 this.currentSize.width == cs.width &&
34526 this.currentSize.x == cs.x &&
34527 this.currentSize.height == cs.height &&
34528 this.currentSize.y == cs.y
34530 Roo.log("no change in with or X or Y");
34534 this.currentSize = cs;
34540 layout : function()
34542 this._resetLayout();
34544 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34546 this.layoutItems( isInstant );
34548 this._isLayoutInited = true;
34550 this.fireEvent('layout', this);
34554 _resetLayout : function()
34556 if(this.isHorizontal){
34557 this.horizontalMeasureColumns();
34561 this.verticalMeasureColumns();
34565 verticalMeasureColumns : function()
34567 this.getContainerWidth();
34569 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34570 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34574 var boxWidth = this.boxWidth + this.padWidth;
34576 if(this.containerWidth < this.boxWidth){
34577 boxWidth = this.containerWidth
34580 var containerWidth = this.containerWidth;
34582 var cols = Math.floor(containerWidth / boxWidth);
34584 this.cols = Math.max( cols, 1 );
34586 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34588 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34590 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34592 this.colWidth = boxWidth + avail - this.padWidth;
34594 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34595 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34598 horizontalMeasureColumns : function()
34600 this.getContainerWidth();
34602 var boxWidth = this.boxWidth;
34604 if(this.containerWidth < boxWidth){
34605 boxWidth = this.containerWidth;
34608 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34610 this.el.setHeight(boxWidth);
34614 getContainerWidth : function()
34616 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34619 layoutItems : function( isInstant )
34621 Roo.log(this.bricks);
34623 var items = Roo.apply([], this.bricks);
34625 if(this.isHorizontal){
34626 this._horizontalLayoutItems( items , isInstant );
34630 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34631 // this._verticalAlternativeLayoutItems( items , isInstant );
34635 this._verticalLayoutItems( items , isInstant );
34639 _verticalLayoutItems : function ( items , isInstant)
34641 if ( !items || !items.length ) {
34646 ['xs', 'xs', 'xs', 'tall'],
34647 ['xs', 'xs', 'tall'],
34648 ['xs', 'xs', 'sm'],
34649 ['xs', 'xs', 'xs'],
34655 ['sm', 'xs', 'xs'],
34659 ['tall', 'xs', 'xs', 'xs'],
34660 ['tall', 'xs', 'xs'],
34672 Roo.each(items, function(item, k){
34674 switch (item.size) {
34675 // these layouts take up a full box,
34686 boxes.push([item]);
34709 var filterPattern = function(box, length)
34717 var pattern = box.slice(0, length);
34721 Roo.each(pattern, function(i){
34722 format.push(i.size);
34725 Roo.each(standard, function(s){
34727 if(String(s) != String(format)){
34736 if(!match && length == 1){
34741 filterPattern(box, length - 1);
34745 queue.push(pattern);
34747 box = box.slice(length, box.length);
34749 filterPattern(box, 4);
34755 Roo.each(boxes, function(box, k){
34761 if(box.length == 1){
34766 filterPattern(box, 4);
34770 this._processVerticalLayoutQueue( queue, isInstant );
34774 // _verticalAlternativeLayoutItems : function( items , isInstant )
34776 // if ( !items || !items.length ) {
34780 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34784 _horizontalLayoutItems : function ( items , isInstant)
34786 if ( !items || !items.length || items.length < 3) {
34792 var eItems = items.slice(0, 3);
34794 items = items.slice(3, items.length);
34797 ['xs', 'xs', 'xs', 'wide'],
34798 ['xs', 'xs', 'wide'],
34799 ['xs', 'xs', 'sm'],
34800 ['xs', 'xs', 'xs'],
34806 ['sm', 'xs', 'xs'],
34810 ['wide', 'xs', 'xs', 'xs'],
34811 ['wide', 'xs', 'xs'],
34824 Roo.each(items, function(item, k){
34826 switch (item.size) {
34837 boxes.push([item]);
34861 var filterPattern = function(box, length)
34869 var pattern = box.slice(0, length);
34873 Roo.each(pattern, function(i){
34874 format.push(i.size);
34877 Roo.each(standard, function(s){
34879 if(String(s) != String(format)){
34888 if(!match && length == 1){
34893 filterPattern(box, length - 1);
34897 queue.push(pattern);
34899 box = box.slice(length, box.length);
34901 filterPattern(box, 4);
34907 Roo.each(boxes, function(box, k){
34913 if(box.length == 1){
34918 filterPattern(box, 4);
34925 var pos = this.el.getBox(true);
34929 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34931 var hit_end = false;
34933 Roo.each(queue, function(box){
34937 Roo.each(box, function(b){
34939 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34949 Roo.each(box, function(b){
34951 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34954 mx = Math.max(mx, b.x);
34958 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34962 Roo.each(box, function(b){
34964 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34978 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34981 /** Sets position of item in DOM
34982 * @param {Element} item
34983 * @param {Number} x - horizontal position
34984 * @param {Number} y - vertical position
34985 * @param {Boolean} isInstant - disables transitions
34987 _processVerticalLayoutQueue : function( queue, isInstant )
34989 var pos = this.el.getBox(true);
34994 for (var i = 0; i < this.cols; i++){
34998 Roo.each(queue, function(box, k){
35000 var col = k % this.cols;
35002 Roo.each(box, function(b,kk){
35004 b.el.position('absolute');
35006 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35007 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35009 if(b.size == 'md-left' || b.size == 'md-right'){
35010 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35011 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35014 b.el.setWidth(width);
35015 b.el.setHeight(height);
35017 b.el.select('iframe',true).setSize(width,height);
35021 for (var i = 0; i < this.cols; i++){
35023 if(maxY[i] < maxY[col]){
35028 col = Math.min(col, i);
35032 x = pos.x + col * (this.colWidth + this.padWidth);
35036 var positions = [];
35038 switch (box.length){
35040 positions = this.getVerticalOneBoxColPositions(x, y, box);
35043 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35046 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35049 positions = this.getVerticalFourBoxColPositions(x, y, box);
35055 Roo.each(box, function(b,kk){
35057 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35059 var sz = b.el.getSize();
35061 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35069 for (var i = 0; i < this.cols; i++){
35070 mY = Math.max(mY, maxY[i]);
35073 this.el.setHeight(mY - pos.y);
35077 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35079 // var pos = this.el.getBox(true);
35082 // var maxX = pos.right;
35084 // var maxHeight = 0;
35086 // Roo.each(items, function(item, k){
35090 // item.el.position('absolute');
35092 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35094 // item.el.setWidth(width);
35096 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35098 // item.el.setHeight(height);
35101 // item.el.setXY([x, y], isInstant ? false : true);
35103 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35106 // y = y + height + this.alternativePadWidth;
35108 // maxHeight = maxHeight + height + this.alternativePadWidth;
35112 // this.el.setHeight(maxHeight);
35116 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35118 var pos = this.el.getBox(true);
35123 var maxX = pos.right;
35125 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35127 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35129 Roo.each(queue, function(box, k){
35131 Roo.each(box, function(b, kk){
35133 b.el.position('absolute');
35135 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35136 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35138 if(b.size == 'md-left' || b.size == 'md-right'){
35139 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35140 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35143 b.el.setWidth(width);
35144 b.el.setHeight(height);
35152 var positions = [];
35154 switch (box.length){
35156 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35159 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35162 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35165 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35171 Roo.each(box, function(b,kk){
35173 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35175 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35183 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35185 Roo.each(eItems, function(b,k){
35187 b.size = (k == 0) ? 'sm' : 'xs';
35188 b.x = (k == 0) ? 2 : 1;
35189 b.y = (k == 0) ? 2 : 1;
35191 b.el.position('absolute');
35193 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35195 b.el.setWidth(width);
35197 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35199 b.el.setHeight(height);
35203 var positions = [];
35206 x : maxX - this.unitWidth * 2 - this.gutter,
35211 x : maxX - this.unitWidth,
35212 y : minY + (this.unitWidth + this.gutter) * 2
35216 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35220 Roo.each(eItems, function(b,k){
35222 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35228 getVerticalOneBoxColPositions : function(x, y, box)
35232 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35234 if(box[0].size == 'md-left'){
35238 if(box[0].size == 'md-right'){
35243 x : x + (this.unitWidth + this.gutter) * rand,
35250 getVerticalTwoBoxColPositions : function(x, y, box)
35254 if(box[0].size == 'xs'){
35258 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35262 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35276 x : x + (this.unitWidth + this.gutter) * 2,
35277 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35284 getVerticalThreeBoxColPositions : function(x, y, box)
35288 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35296 x : x + (this.unitWidth + this.gutter) * 1,
35301 x : x + (this.unitWidth + this.gutter) * 2,
35309 if(box[0].size == 'xs' && box[1].size == 'xs'){
35318 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35322 x : x + (this.unitWidth + this.gutter) * 1,
35336 x : x + (this.unitWidth + this.gutter) * 2,
35341 x : x + (this.unitWidth + this.gutter) * 2,
35342 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35349 getVerticalFourBoxColPositions : function(x, y, box)
35353 if(box[0].size == 'xs'){
35362 y : y + (this.unitHeight + this.gutter) * 1
35367 y : y + (this.unitHeight + this.gutter) * 2
35371 x : x + (this.unitWidth + this.gutter) * 1,
35385 x : x + (this.unitWidth + this.gutter) * 2,
35390 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35391 y : y + (this.unitHeight + this.gutter) * 1
35395 x : x + (this.unitWidth + this.gutter) * 2,
35396 y : y + (this.unitWidth + this.gutter) * 2
35403 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35407 if(box[0].size == 'md-left'){
35409 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35416 if(box[0].size == 'md-right'){
35418 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35419 y : minY + (this.unitWidth + this.gutter) * 1
35425 var rand = Math.floor(Math.random() * (4 - box[0].y));
35428 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35429 y : minY + (this.unitWidth + this.gutter) * rand
35436 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35440 if(box[0].size == 'xs'){
35443 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35448 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35449 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35457 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35462 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35463 y : minY + (this.unitWidth + this.gutter) * 2
35470 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35474 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35477 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35482 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35483 y : minY + (this.unitWidth + this.gutter) * 1
35487 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35488 y : minY + (this.unitWidth + this.gutter) * 2
35495 if(box[0].size == 'xs' && box[1].size == 'xs'){
35498 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35503 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35508 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35509 y : minY + (this.unitWidth + this.gutter) * 1
35517 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35522 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35523 y : minY + (this.unitWidth + this.gutter) * 2
35527 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35528 y : minY + (this.unitWidth + this.gutter) * 2
35535 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35539 if(box[0].size == 'xs'){
35542 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35547 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35552 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),
35557 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35558 y : minY + (this.unitWidth + this.gutter) * 1
35566 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35571 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35572 y : minY + (this.unitWidth + this.gutter) * 2
35576 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35577 y : minY + (this.unitWidth + this.gutter) * 2
35581 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),
35582 y : minY + (this.unitWidth + this.gutter) * 2
35590 * remove a Masonry Brick
35591 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35593 removeBrick : function(brick_id)
35599 for (var i = 0; i<this.bricks.length; i++) {
35600 if (this.bricks[i].id == brick_id) {
35601 this.bricks.splice(i,1);
35602 this.el.dom.removeChild(Roo.get(brick_id).dom);
35609 * adds a Masonry Brick
35610 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35612 addBrick : function(cfg)
35614 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35615 //this.register(cn);
35616 cn.parentId = this.id;
35617 cn.render(this.el);
35622 * register a Masonry Brick
35623 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35626 register : function(brick)
35628 this.bricks.push(brick);
35629 brick.masonryId = this.id;
35633 * clear all the Masonry Brick
35635 clearAll : function()
35638 //this.getChildContainer().dom.innerHTML = "";
35639 this.el.dom.innerHTML = '';
35642 getSelected : function()
35644 if (!this.selectedBrick) {
35648 return this.selectedBrick;
35652 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35656 * register a Masonry Layout
35657 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35660 register : function(layout)
35662 this.groups[layout.id] = layout;
35665 * fetch a Masonry Layout based on the masonry layout ID
35666 * @param {string} the masonry layout to add
35667 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35670 get: function(layout_id) {
35671 if (typeof(this.groups[layout_id]) == 'undefined') {
35674 return this.groups[layout_id] ;
35686 * http://masonry.desandro.com
35688 * The idea is to render all the bricks based on vertical width...
35690 * The original code extends 'outlayer' - we might need to use that....
35696 * @class Roo.bootstrap.LayoutMasonryAuto
35697 * @extends Roo.bootstrap.Component
35698 * Bootstrap Layout Masonry class
35701 * Create a new Element
35702 * @param {Object} config The config object
35705 Roo.bootstrap.LayoutMasonryAuto = function(config){
35706 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35709 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35712 * @cfg {Boolean} isFitWidth - resize the width..
35714 isFitWidth : false, // options..
35716 * @cfg {Boolean} isOriginLeft = left align?
35718 isOriginLeft : true,
35720 * @cfg {Boolean} isOriginTop = top align?
35722 isOriginTop : false,
35724 * @cfg {Boolean} isLayoutInstant = no animation?
35726 isLayoutInstant : false, // needed?
35728 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35730 isResizingContainer : true,
35732 * @cfg {Number} columnWidth width of the columns
35738 * @cfg {Number} maxCols maximum number of columns
35743 * @cfg {Number} padHeight padding below box..
35749 * @cfg {Boolean} isAutoInitial defalut true
35752 isAutoInitial : true,
35758 initialColumnWidth : 0,
35759 currentSize : null,
35761 colYs : null, // array.
35768 bricks: null, //CompositeElement
35769 cols : 0, // array?
35770 // element : null, // wrapped now this.el
35771 _isLayoutInited : null,
35774 getAutoCreate : function(){
35778 cls: 'blog-masonary-wrapper ' + this.cls,
35780 cls : 'mas-boxes masonary'
35787 getChildContainer: function( )
35789 if (this.boxesEl) {
35790 return this.boxesEl;
35793 this.boxesEl = this.el.select('.mas-boxes').first();
35795 return this.boxesEl;
35799 initEvents : function()
35803 if(this.isAutoInitial){
35804 Roo.log('hook children rendered');
35805 this.on('childrenrendered', function() {
35806 Roo.log('children rendered');
35813 initial : function()
35815 this.reloadItems();
35817 this.currentSize = this.el.getBox(true);
35819 /// was window resize... - let's see if this works..
35820 Roo.EventManager.onWindowResize(this.resize, this);
35822 if(!this.isAutoInitial){
35827 this.layout.defer(500,this);
35830 reloadItems: function()
35832 this.bricks = this.el.select('.masonry-brick', true);
35834 this.bricks.each(function(b) {
35835 //Roo.log(b.getSize());
35836 if (!b.attr('originalwidth')) {
35837 b.attr('originalwidth', b.getSize().width);
35842 Roo.log(this.bricks.elements.length);
35845 resize : function()
35848 var cs = this.el.getBox(true);
35850 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35851 Roo.log("no change in with or X");
35854 this.currentSize = cs;
35858 layout : function()
35861 this._resetLayout();
35862 //this._manageStamps();
35864 // don't animate first layout
35865 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35866 this.layoutItems( isInstant );
35868 // flag for initalized
35869 this._isLayoutInited = true;
35872 layoutItems : function( isInstant )
35874 //var items = this._getItemsForLayout( this.items );
35875 // original code supports filtering layout items.. we just ignore it..
35877 this._layoutItems( this.bricks , isInstant );
35879 this._postLayout();
35881 _layoutItems : function ( items , isInstant)
35883 //this.fireEvent( 'layout', this, items );
35886 if ( !items || !items.elements.length ) {
35887 // no items, emit event with empty array
35892 items.each(function(item) {
35893 Roo.log("layout item");
35895 // get x/y object from method
35896 var position = this._getItemLayoutPosition( item );
35898 position.item = item;
35899 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35900 queue.push( position );
35903 this._processLayoutQueue( queue );
35905 /** Sets position of item in DOM
35906 * @param {Element} item
35907 * @param {Number} x - horizontal position
35908 * @param {Number} y - vertical position
35909 * @param {Boolean} isInstant - disables transitions
35911 _processLayoutQueue : function( queue )
35913 for ( var i=0, len = queue.length; i < len; i++ ) {
35914 var obj = queue[i];
35915 obj.item.position('absolute');
35916 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35922 * Any logic you want to do after each layout,
35923 * i.e. size the container
35925 _postLayout : function()
35927 this.resizeContainer();
35930 resizeContainer : function()
35932 if ( !this.isResizingContainer ) {
35935 var size = this._getContainerSize();
35937 this.el.setSize(size.width,size.height);
35938 this.boxesEl.setSize(size.width,size.height);
35944 _resetLayout : function()
35946 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35947 this.colWidth = this.el.getWidth();
35948 //this.gutter = this.el.getWidth();
35950 this.measureColumns();
35956 this.colYs.push( 0 );
35962 measureColumns : function()
35964 this.getContainerWidth();
35965 // if columnWidth is 0, default to outerWidth of first item
35966 if ( !this.columnWidth ) {
35967 var firstItem = this.bricks.first();
35968 Roo.log(firstItem);
35969 this.columnWidth = this.containerWidth;
35970 if (firstItem && firstItem.attr('originalwidth') ) {
35971 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35973 // columnWidth fall back to item of first element
35974 Roo.log("set column width?");
35975 this.initialColumnWidth = this.columnWidth ;
35977 // if first elem has no width, default to size of container
35982 if (this.initialColumnWidth) {
35983 this.columnWidth = this.initialColumnWidth;
35988 // column width is fixed at the top - however if container width get's smaller we should
35991 // this bit calcs how man columns..
35993 var columnWidth = this.columnWidth += this.gutter;
35995 // calculate columns
35996 var containerWidth = this.containerWidth + this.gutter;
35998 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35999 // fix rounding errors, typically with gutters
36000 var excess = columnWidth - containerWidth % columnWidth;
36003 // if overshoot is less than a pixel, round up, otherwise floor it
36004 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36005 cols = Math[ mathMethod ]( cols );
36006 this.cols = Math.max( cols, 1 );
36007 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36009 // padding positioning..
36010 var totalColWidth = this.cols * this.columnWidth;
36011 var padavail = this.containerWidth - totalColWidth;
36012 // so for 2 columns - we need 3 'pads'
36014 var padNeeded = (1+this.cols) * this.padWidth;
36016 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36018 this.columnWidth += padExtra
36019 //this.padWidth = Math.floor(padavail / ( this.cols));
36021 // adjust colum width so that padding is fixed??
36023 // we have 3 columns ... total = width * 3
36024 // we have X left over... that should be used by
36026 //if (this.expandC) {
36034 getContainerWidth : function()
36036 /* // container is parent if fit width
36037 var container = this.isFitWidth ? this.element.parentNode : this.element;
36038 // check that this.size and size are there
36039 // IE8 triggers resize on body size change, so they might not be
36041 var size = getSize( container ); //FIXME
36042 this.containerWidth = size && size.innerWidth; //FIXME
36045 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36049 _getItemLayoutPosition : function( item ) // what is item?
36051 // we resize the item to our columnWidth..
36053 item.setWidth(this.columnWidth);
36054 item.autoBoxAdjust = false;
36056 var sz = item.getSize();
36058 // how many columns does this brick span
36059 var remainder = this.containerWidth % this.columnWidth;
36061 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36062 // round if off by 1 pixel, otherwise use ceil
36063 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36064 colSpan = Math.min( colSpan, this.cols );
36066 // normally this should be '1' as we dont' currently allow multi width columns..
36068 var colGroup = this._getColGroup( colSpan );
36069 // get the minimum Y value from the columns
36070 var minimumY = Math.min.apply( Math, colGroup );
36071 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36073 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36075 // position the brick
36077 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36078 y: this.currentSize.y + minimumY + this.padHeight
36082 // apply setHeight to necessary columns
36083 var setHeight = minimumY + sz.height + this.padHeight;
36084 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36086 var setSpan = this.cols + 1 - colGroup.length;
36087 for ( var i = 0; i < setSpan; i++ ) {
36088 this.colYs[ shortColIndex + i ] = setHeight ;
36095 * @param {Number} colSpan - number of columns the element spans
36096 * @returns {Array} colGroup
36098 _getColGroup : function( colSpan )
36100 if ( colSpan < 2 ) {
36101 // if brick spans only one column, use all the column Ys
36106 // how many different places could this brick fit horizontally
36107 var groupCount = this.cols + 1 - colSpan;
36108 // for each group potential horizontal position
36109 for ( var i = 0; i < groupCount; i++ ) {
36110 // make an array of colY values for that one group
36111 var groupColYs = this.colYs.slice( i, i + colSpan );
36112 // and get the max value of the array
36113 colGroup[i] = Math.max.apply( Math, groupColYs );
36118 _manageStamp : function( stamp )
36120 var stampSize = stamp.getSize();
36121 var offset = stamp.getBox();
36122 // get the columns that this stamp affects
36123 var firstX = this.isOriginLeft ? offset.x : offset.right;
36124 var lastX = firstX + stampSize.width;
36125 var firstCol = Math.floor( firstX / this.columnWidth );
36126 firstCol = Math.max( 0, firstCol );
36128 var lastCol = Math.floor( lastX / this.columnWidth );
36129 // lastCol should not go over if multiple of columnWidth #425
36130 lastCol -= lastX % this.columnWidth ? 0 : 1;
36131 lastCol = Math.min( this.cols - 1, lastCol );
36133 // set colYs to bottom of the stamp
36134 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36137 for ( var i = firstCol; i <= lastCol; i++ ) {
36138 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36143 _getContainerSize : function()
36145 this.maxY = Math.max.apply( Math, this.colYs );
36150 if ( this.isFitWidth ) {
36151 size.width = this._getContainerFitWidth();
36157 _getContainerFitWidth : function()
36159 var unusedCols = 0;
36160 // count unused columns
36163 if ( this.colYs[i] !== 0 ) {
36168 // fit container to columns that have been used
36169 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36172 needsResizeLayout : function()
36174 var previousWidth = this.containerWidth;
36175 this.getContainerWidth();
36176 return previousWidth !== this.containerWidth;
36191 * @class Roo.bootstrap.MasonryBrick
36192 * @extends Roo.bootstrap.Component
36193 * Bootstrap MasonryBrick class
36196 * Create a new MasonryBrick
36197 * @param {Object} config The config object
36200 Roo.bootstrap.MasonryBrick = function(config){
36202 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36204 Roo.bootstrap.MasonryBrick.register(this);
36210 * When a MasonryBrick is clcik
36211 * @param {Roo.bootstrap.MasonryBrick} this
36212 * @param {Roo.EventObject} e
36218 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36221 * @cfg {String} title
36225 * @cfg {String} html
36229 * @cfg {String} bgimage
36233 * @cfg {String} videourl
36237 * @cfg {String} cls
36241 * @cfg {String} href
36245 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36250 * @cfg {String} placetitle (center|bottom)
36255 * @cfg {Boolean} isFitContainer defalut true
36257 isFitContainer : true,
36260 * @cfg {Boolean} preventDefault defalut false
36262 preventDefault : false,
36265 * @cfg {Boolean} inverse defalut false
36267 maskInverse : false,
36269 getAutoCreate : function()
36271 if(!this.isFitContainer){
36272 return this.getSplitAutoCreate();
36275 var cls = 'masonry-brick masonry-brick-full';
36277 if(this.href.length){
36278 cls += ' masonry-brick-link';
36281 if(this.bgimage.length){
36282 cls += ' masonry-brick-image';
36285 if(this.maskInverse){
36286 cls += ' mask-inverse';
36289 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36290 cls += ' enable-mask';
36294 cls += ' masonry-' + this.size + '-brick';
36297 if(this.placetitle.length){
36299 switch (this.placetitle) {
36301 cls += ' masonry-center-title';
36304 cls += ' masonry-bottom-title';
36311 if(!this.html.length && !this.bgimage.length){
36312 cls += ' masonry-center-title';
36315 if(!this.html.length && this.bgimage.length){
36316 cls += ' masonry-bottom-title';
36321 cls += ' ' + this.cls;
36325 tag: (this.href.length) ? 'a' : 'div',
36330 cls: 'masonry-brick-mask'
36334 cls: 'masonry-brick-paragraph',
36340 if(this.href.length){
36341 cfg.href = this.href;
36344 var cn = cfg.cn[1].cn;
36346 if(this.title.length){
36349 cls: 'masonry-brick-title',
36354 if(this.html.length){
36357 cls: 'masonry-brick-text',
36362 if (!this.title.length && !this.html.length) {
36363 cfg.cn[1].cls += ' hide';
36366 if(this.bgimage.length){
36369 cls: 'masonry-brick-image-view',
36374 if(this.videourl.length){
36375 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36376 // youtube support only?
36379 cls: 'masonry-brick-image-view',
36382 allowfullscreen : true
36390 getSplitAutoCreate : function()
36392 var cls = 'masonry-brick masonry-brick-split';
36394 if(this.href.length){
36395 cls += ' masonry-brick-link';
36398 if(this.bgimage.length){
36399 cls += ' masonry-brick-image';
36403 cls += ' masonry-' + this.size + '-brick';
36406 switch (this.placetitle) {
36408 cls += ' masonry-center-title';
36411 cls += ' masonry-bottom-title';
36414 if(!this.bgimage.length){
36415 cls += ' masonry-center-title';
36418 if(this.bgimage.length){
36419 cls += ' masonry-bottom-title';
36425 cls += ' ' + this.cls;
36429 tag: (this.href.length) ? 'a' : 'div',
36434 cls: 'masonry-brick-split-head',
36438 cls: 'masonry-brick-paragraph',
36445 cls: 'masonry-brick-split-body',
36451 if(this.href.length){
36452 cfg.href = this.href;
36455 if(this.title.length){
36456 cfg.cn[0].cn[0].cn.push({
36458 cls: 'masonry-brick-title',
36463 if(this.html.length){
36464 cfg.cn[1].cn.push({
36466 cls: 'masonry-brick-text',
36471 if(this.bgimage.length){
36472 cfg.cn[0].cn.push({
36474 cls: 'masonry-brick-image-view',
36479 if(this.videourl.length){
36480 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36481 // youtube support only?
36482 cfg.cn[0].cn.cn.push({
36484 cls: 'masonry-brick-image-view',
36487 allowfullscreen : true
36494 initEvents: function()
36496 switch (this.size) {
36529 this.el.on('touchstart', this.onTouchStart, this);
36530 this.el.on('touchmove', this.onTouchMove, this);
36531 this.el.on('touchend', this.onTouchEnd, this);
36532 this.el.on('contextmenu', this.onContextMenu, this);
36534 this.el.on('mouseenter' ,this.enter, this);
36535 this.el.on('mouseleave', this.leave, this);
36536 this.el.on('click', this.onClick, this);
36539 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36540 this.parent().bricks.push(this);
36545 onClick: function(e, el)
36547 var time = this.endTimer - this.startTimer;
36548 // Roo.log(e.preventDefault());
36551 e.preventDefault();
36556 if(!this.preventDefault){
36560 e.preventDefault();
36562 if (this.activeClass != '') {
36563 this.selectBrick();
36566 this.fireEvent('click', this, e);
36569 enter: function(e, el)
36571 e.preventDefault();
36573 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36577 if(this.bgimage.length && this.html.length){
36578 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36582 leave: function(e, el)
36584 e.preventDefault();
36586 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36590 if(this.bgimage.length && this.html.length){
36591 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36595 onTouchStart: function(e, el)
36597 // e.preventDefault();
36599 this.touchmoved = false;
36601 if(!this.isFitContainer){
36605 if(!this.bgimage.length || !this.html.length){
36609 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36611 this.timer = new Date().getTime();
36615 onTouchMove: function(e, el)
36617 this.touchmoved = true;
36620 onContextMenu : function(e,el)
36622 e.preventDefault();
36623 e.stopPropagation();
36627 onTouchEnd: function(e, el)
36629 // e.preventDefault();
36631 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36638 if(!this.bgimage.length || !this.html.length){
36640 if(this.href.length){
36641 window.location.href = this.href;
36647 if(!this.isFitContainer){
36651 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36653 window.location.href = this.href;
36656 //selection on single brick only
36657 selectBrick : function() {
36659 if (!this.parentId) {
36663 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36664 var index = m.selectedBrick.indexOf(this.id);
36667 m.selectedBrick.splice(index,1);
36668 this.el.removeClass(this.activeClass);
36672 for(var i = 0; i < m.selectedBrick.length; i++) {
36673 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36674 b.el.removeClass(b.activeClass);
36677 m.selectedBrick = [];
36679 m.selectedBrick.push(this.id);
36680 this.el.addClass(this.activeClass);
36684 isSelected : function(){
36685 return this.el.hasClass(this.activeClass);
36690 Roo.apply(Roo.bootstrap.MasonryBrick, {
36693 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36695 * register a Masonry Brick
36696 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36699 register : function(brick)
36701 //this.groups[brick.id] = brick;
36702 this.groups.add(brick.id, brick);
36705 * fetch a masonry brick based on the masonry brick ID
36706 * @param {string} the masonry brick to add
36707 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36710 get: function(brick_id)
36712 // if (typeof(this.groups[brick_id]) == 'undefined') {
36715 // return this.groups[brick_id] ;
36717 if(this.groups.key(brick_id)) {
36718 return this.groups.key(brick_id);
36736 * @class Roo.bootstrap.Brick
36737 * @extends Roo.bootstrap.Component
36738 * Bootstrap Brick class
36741 * Create a new Brick
36742 * @param {Object} config The config object
36745 Roo.bootstrap.Brick = function(config){
36746 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36752 * When a Brick is click
36753 * @param {Roo.bootstrap.Brick} this
36754 * @param {Roo.EventObject} e
36760 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36763 * @cfg {String} title
36767 * @cfg {String} html
36771 * @cfg {String} bgimage
36775 * @cfg {String} cls
36779 * @cfg {String} href
36783 * @cfg {String} video
36787 * @cfg {Boolean} square
36791 getAutoCreate : function()
36793 var cls = 'roo-brick';
36795 if(this.href.length){
36796 cls += ' roo-brick-link';
36799 if(this.bgimage.length){
36800 cls += ' roo-brick-image';
36803 if(!this.html.length && !this.bgimage.length){
36804 cls += ' roo-brick-center-title';
36807 if(!this.html.length && this.bgimage.length){
36808 cls += ' roo-brick-bottom-title';
36812 cls += ' ' + this.cls;
36816 tag: (this.href.length) ? 'a' : 'div',
36821 cls: 'roo-brick-paragraph',
36827 if(this.href.length){
36828 cfg.href = this.href;
36831 var cn = cfg.cn[0].cn;
36833 if(this.title.length){
36836 cls: 'roo-brick-title',
36841 if(this.html.length){
36844 cls: 'roo-brick-text',
36851 if(this.bgimage.length){
36854 cls: 'roo-brick-image-view',
36862 initEvents: function()
36864 if(this.title.length || this.html.length){
36865 this.el.on('mouseenter' ,this.enter, this);
36866 this.el.on('mouseleave', this.leave, this);
36869 Roo.EventManager.onWindowResize(this.resize, this);
36871 if(this.bgimage.length){
36872 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36873 this.imageEl.on('load', this.onImageLoad, this);
36880 onImageLoad : function()
36885 resize : function()
36887 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36889 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36891 if(this.bgimage.length){
36892 var image = this.el.select('.roo-brick-image-view', true).first();
36894 image.setWidth(paragraph.getWidth());
36897 image.setHeight(paragraph.getWidth());
36900 this.el.setHeight(image.getHeight());
36901 paragraph.setHeight(image.getHeight());
36907 enter: function(e, el)
36909 e.preventDefault();
36911 if(this.bgimage.length){
36912 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36913 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36917 leave: function(e, el)
36919 e.preventDefault();
36921 if(this.bgimage.length){
36922 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36923 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36938 * @class Roo.bootstrap.NumberField
36939 * @extends Roo.bootstrap.Input
36940 * Bootstrap NumberField class
36946 * Create a new NumberField
36947 * @param {Object} config The config object
36950 Roo.bootstrap.NumberField = function(config){
36951 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36954 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36957 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36959 allowDecimals : true,
36961 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36963 decimalSeparator : ".",
36965 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36967 decimalPrecision : 2,
36969 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36971 allowNegative : true,
36974 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36978 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36980 minValue : Number.NEGATIVE_INFINITY,
36982 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36984 maxValue : Number.MAX_VALUE,
36986 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36988 minText : "The minimum value for this field is {0}",
36990 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36992 maxText : "The maximum value for this field is {0}",
36994 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36995 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36997 nanText : "{0} is not a valid number",
36999 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37001 thousandsDelimiter : false,
37003 * @cfg {String} valueAlign alignment of value
37005 valueAlign : "left",
37007 getAutoCreate : function()
37009 var hiddenInput = {
37013 cls: 'hidden-number-input'
37017 hiddenInput.name = this.name;
37022 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37024 this.name = hiddenInput.name;
37026 if(cfg.cn.length > 0) {
37027 cfg.cn.push(hiddenInput);
37034 initEvents : function()
37036 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37038 var allowed = "0123456789";
37040 if(this.allowDecimals){
37041 allowed += this.decimalSeparator;
37044 if(this.allowNegative){
37048 if(this.thousandsDelimiter) {
37052 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37054 var keyPress = function(e){
37056 var k = e.getKey();
37058 var c = e.getCharCode();
37061 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37062 allowed.indexOf(String.fromCharCode(c)) === -1
37068 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37072 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37077 this.el.on("keypress", keyPress, this);
37080 validateValue : function(value)
37083 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37087 var num = this.parseValue(value);
37090 this.markInvalid(String.format(this.nanText, value));
37094 if(num < this.minValue){
37095 this.markInvalid(String.format(this.minText, this.minValue));
37099 if(num > this.maxValue){
37100 this.markInvalid(String.format(this.maxText, this.maxValue));
37107 getValue : function()
37109 var v = this.hiddenEl().getValue();
37111 return this.fixPrecision(this.parseValue(v));
37114 parseValue : function(value)
37116 if(this.thousandsDelimiter) {
37118 r = new RegExp(",", "g");
37119 value = value.replace(r, "");
37122 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37123 return isNaN(value) ? '' : value;
37126 fixPrecision : function(value)
37128 if(this.thousandsDelimiter) {
37130 r = new RegExp(",", "g");
37131 value = value.replace(r, "");
37134 var nan = isNaN(value);
37136 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37137 return nan ? '' : value;
37139 return parseFloat(value).toFixed(this.decimalPrecision);
37142 setValue : function(v)
37144 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37150 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37152 this.inputEl().dom.value = (v == '') ? '' :
37153 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37155 if(!this.allowZero && v === '0') {
37156 this.hiddenEl().dom.value = '';
37157 this.inputEl().dom.value = '';
37164 decimalPrecisionFcn : function(v)
37166 return Math.floor(v);
37169 beforeBlur : function()
37171 var v = this.parseValue(this.getRawValue());
37173 if(v || v === 0 || v === ''){
37178 hiddenEl : function()
37180 return this.el.select('input.hidden-number-input',true).first();
37192 * @class Roo.bootstrap.DocumentSlider
37193 * @extends Roo.bootstrap.Component
37194 * Bootstrap DocumentSlider class
37197 * Create a new DocumentViewer
37198 * @param {Object} config The config object
37201 Roo.bootstrap.DocumentSlider = function(config){
37202 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37209 * Fire after initEvent
37210 * @param {Roo.bootstrap.DocumentSlider} this
37215 * Fire after update
37216 * @param {Roo.bootstrap.DocumentSlider} this
37222 * @param {Roo.bootstrap.DocumentSlider} this
37228 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37234 getAutoCreate : function()
37238 cls : 'roo-document-slider',
37242 cls : 'roo-document-slider-header',
37246 cls : 'roo-document-slider-header-title'
37252 cls : 'roo-document-slider-body',
37256 cls : 'roo-document-slider-prev',
37260 cls : 'fa fa-chevron-left'
37266 cls : 'roo-document-slider-thumb',
37270 cls : 'roo-document-slider-image'
37276 cls : 'roo-document-slider-next',
37280 cls : 'fa fa-chevron-right'
37292 initEvents : function()
37294 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37295 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37297 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37298 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37300 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37301 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37303 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37304 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37306 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37307 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37309 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37310 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37312 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37313 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37315 this.thumbEl.on('click', this.onClick, this);
37317 this.prevIndicator.on('click', this.prev, this);
37319 this.nextIndicator.on('click', this.next, this);
37323 initial : function()
37325 if(this.files.length){
37326 this.indicator = 1;
37330 this.fireEvent('initial', this);
37333 update : function()
37335 this.imageEl.attr('src', this.files[this.indicator - 1]);
37337 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37339 this.prevIndicator.show();
37341 if(this.indicator == 1){
37342 this.prevIndicator.hide();
37345 this.nextIndicator.show();
37347 if(this.indicator == this.files.length){
37348 this.nextIndicator.hide();
37351 this.thumbEl.scrollTo('top');
37353 this.fireEvent('update', this);
37356 onClick : function(e)
37358 e.preventDefault();
37360 this.fireEvent('click', this);
37365 e.preventDefault();
37367 this.indicator = Math.max(1, this.indicator - 1);
37374 e.preventDefault();
37376 this.indicator = Math.min(this.files.length, this.indicator + 1);
37390 * @class Roo.bootstrap.RadioSet
37391 * @extends Roo.bootstrap.Input
37392 * Bootstrap RadioSet class
37393 * @cfg {String} indicatorpos (left|right) default left
37394 * @cfg {Boolean} inline (true|false) inline the element (default true)
37395 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37397 * Create a new RadioSet
37398 * @param {Object} config The config object
37401 Roo.bootstrap.RadioSet = function(config){
37403 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37407 Roo.bootstrap.RadioSet.register(this);
37412 * Fires when the element is checked or unchecked.
37413 * @param {Roo.bootstrap.RadioSet} this This radio
37414 * @param {Roo.bootstrap.Radio} item The checked item
37419 * Fires when the element is click.
37420 * @param {Roo.bootstrap.RadioSet} this This radio set
37421 * @param {Roo.bootstrap.Radio} item The checked item
37422 * @param {Roo.EventObject} e The event object
37429 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37437 indicatorpos : 'left',
37439 getAutoCreate : function()
37443 cls : 'roo-radio-set-label',
37447 html : this.fieldLabel
37451 if (Roo.bootstrap.version == 3) {
37454 if(this.indicatorpos == 'left'){
37457 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37458 tooltip : 'This field is required'
37463 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37464 tooltip : 'This field is required'
37470 cls : 'roo-radio-set-items'
37473 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37475 if (align === 'left' && this.fieldLabel.length) {
37478 cls : "roo-radio-set-right",
37484 if(this.labelWidth > 12){
37485 label.style = "width: " + this.labelWidth + 'px';
37488 if(this.labelWidth < 13 && this.labelmd == 0){
37489 this.labelmd = this.labelWidth;
37492 if(this.labellg > 0){
37493 label.cls += ' col-lg-' + this.labellg;
37494 items.cls += ' col-lg-' + (12 - this.labellg);
37497 if(this.labelmd > 0){
37498 label.cls += ' col-md-' + this.labelmd;
37499 items.cls += ' col-md-' + (12 - this.labelmd);
37502 if(this.labelsm > 0){
37503 label.cls += ' col-sm-' + this.labelsm;
37504 items.cls += ' col-sm-' + (12 - this.labelsm);
37507 if(this.labelxs > 0){
37508 label.cls += ' col-xs-' + this.labelxs;
37509 items.cls += ' col-xs-' + (12 - this.labelxs);
37515 cls : 'roo-radio-set',
37519 cls : 'roo-radio-set-input',
37522 value : this.value ? this.value : ''
37529 if(this.weight.length){
37530 cfg.cls += ' roo-radio-' + this.weight;
37534 cfg.cls += ' roo-radio-set-inline';
37538 ['xs','sm','md','lg'].map(function(size){
37539 if (settings[size]) {
37540 cfg.cls += ' col-' + size + '-' + settings[size];
37548 initEvents : function()
37550 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37551 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37553 if(!this.fieldLabel.length){
37554 this.labelEl.hide();
37557 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37558 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37560 this.indicator = this.indicatorEl();
37562 if(this.indicator){
37563 this.indicator.addClass('invisible');
37566 this.originalValue = this.getValue();
37570 inputEl: function ()
37572 return this.el.select('.roo-radio-set-input', true).first();
37575 getChildContainer : function()
37577 return this.itemsEl;
37580 register : function(item)
37582 this.radioes.push(item);
37586 validate : function()
37588 if(this.getVisibilityEl().hasClass('hidden')){
37594 Roo.each(this.radioes, function(i){
37603 if(this.allowBlank) {
37607 if(this.disabled || valid){
37612 this.markInvalid();
37617 markValid : function()
37619 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37620 this.indicatorEl().removeClass('visible');
37621 this.indicatorEl().addClass('invisible');
37625 if (Roo.bootstrap.version == 3) {
37626 this.el.removeClass([this.invalidClass, this.validClass]);
37627 this.el.addClass(this.validClass);
37629 this.el.removeClass(['is-invalid','is-valid']);
37630 this.el.addClass(['is-valid']);
37632 this.fireEvent('valid', this);
37635 markInvalid : function(msg)
37637 if(this.allowBlank || this.disabled){
37641 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37642 this.indicatorEl().removeClass('invisible');
37643 this.indicatorEl().addClass('visible');
37645 if (Roo.bootstrap.version == 3) {
37646 this.el.removeClass([this.invalidClass, this.validClass]);
37647 this.el.addClass(this.invalidClass);
37649 this.el.removeClass(['is-invalid','is-valid']);
37650 this.el.addClass(['is-invalid']);
37653 this.fireEvent('invalid', this, msg);
37657 setValue : function(v, suppressEvent)
37659 if(this.value === v){
37666 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37669 Roo.each(this.radioes, function(i){
37671 i.el.removeClass('checked');
37674 Roo.each(this.radioes, function(i){
37676 if(i.value === v || i.value.toString() === v.toString()){
37678 i.el.addClass('checked');
37680 if(suppressEvent !== true){
37681 this.fireEvent('check', this, i);
37692 clearInvalid : function(){
37694 if(!this.el || this.preventMark){
37698 this.el.removeClass([this.invalidClass]);
37700 this.fireEvent('valid', this);
37705 Roo.apply(Roo.bootstrap.RadioSet, {
37709 register : function(set)
37711 this.groups[set.name] = set;
37714 get: function(name)
37716 if (typeof(this.groups[name]) == 'undefined') {
37720 return this.groups[name] ;
37726 * Ext JS Library 1.1.1
37727 * Copyright(c) 2006-2007, Ext JS, LLC.
37729 * Originally Released Under LGPL - original licence link has changed is not relivant.
37732 * <script type="text/javascript">
37737 * @class Roo.bootstrap.SplitBar
37738 * @extends Roo.util.Observable
37739 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37743 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37744 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37745 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37746 split.minSize = 100;
37747 split.maxSize = 600;
37748 split.animate = true;
37749 split.on('moved', splitterMoved);
37752 * Create a new SplitBar
37753 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37754 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37755 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37756 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37757 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37758 position of the SplitBar).
37760 Roo.bootstrap.SplitBar = function(cfg){
37765 // dragElement : elm
37766 // resizingElement: el,
37768 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37769 // placement : Roo.bootstrap.SplitBar.LEFT ,
37770 // existingProxy ???
37773 this.el = Roo.get(cfg.dragElement, true);
37774 this.el.dom.unselectable = "on";
37776 this.resizingEl = Roo.get(cfg.resizingElement, true);
37780 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37781 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37784 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37787 * The minimum size of the resizing element. (Defaults to 0)
37793 * The maximum size of the resizing element. (Defaults to 2000)
37796 this.maxSize = 2000;
37799 * Whether to animate the transition to the new size
37802 this.animate = false;
37805 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37808 this.useShim = false;
37813 if(!cfg.existingProxy){
37815 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37817 this.proxy = Roo.get(cfg.existingProxy).dom;
37820 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37823 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37826 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37829 this.dragSpecs = {};
37832 * @private The adapter to use to positon and resize elements
37834 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37835 this.adapter.init(this);
37837 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37839 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37840 this.el.addClass("roo-splitbar-h");
37843 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37844 this.el.addClass("roo-splitbar-v");
37850 * Fires when the splitter is moved (alias for {@link #event-moved})
37851 * @param {Roo.bootstrap.SplitBar} this
37852 * @param {Number} newSize the new width or height
37857 * Fires when the splitter is moved
37858 * @param {Roo.bootstrap.SplitBar} this
37859 * @param {Number} newSize the new width or height
37863 * @event beforeresize
37864 * Fires before the splitter is dragged
37865 * @param {Roo.bootstrap.SplitBar} this
37867 "beforeresize" : true,
37869 "beforeapply" : true
37872 Roo.util.Observable.call(this);
37875 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37876 onStartProxyDrag : function(x, y){
37877 this.fireEvent("beforeresize", this);
37879 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37881 o.enableDisplayMode("block");
37882 // all splitbars share the same overlay
37883 Roo.bootstrap.SplitBar.prototype.overlay = o;
37885 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37886 this.overlay.show();
37887 Roo.get(this.proxy).setDisplayed("block");
37888 var size = this.adapter.getElementSize(this);
37889 this.activeMinSize = this.getMinimumSize();;
37890 this.activeMaxSize = this.getMaximumSize();;
37891 var c1 = size - this.activeMinSize;
37892 var c2 = Math.max(this.activeMaxSize - size, 0);
37893 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37894 this.dd.resetConstraints();
37895 this.dd.setXConstraint(
37896 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37897 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37899 this.dd.setYConstraint(0, 0);
37901 this.dd.resetConstraints();
37902 this.dd.setXConstraint(0, 0);
37903 this.dd.setYConstraint(
37904 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37905 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37908 this.dragSpecs.startSize = size;
37909 this.dragSpecs.startPoint = [x, y];
37910 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37914 * @private Called after the drag operation by the DDProxy
37916 onEndProxyDrag : function(e){
37917 Roo.get(this.proxy).setDisplayed(false);
37918 var endPoint = Roo.lib.Event.getXY(e);
37920 this.overlay.hide();
37923 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37924 newSize = this.dragSpecs.startSize +
37925 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37926 endPoint[0] - this.dragSpecs.startPoint[0] :
37927 this.dragSpecs.startPoint[0] - endPoint[0]
37930 newSize = this.dragSpecs.startSize +
37931 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37932 endPoint[1] - this.dragSpecs.startPoint[1] :
37933 this.dragSpecs.startPoint[1] - endPoint[1]
37936 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37937 if(newSize != this.dragSpecs.startSize){
37938 if(this.fireEvent('beforeapply', this, newSize) !== false){
37939 this.adapter.setElementSize(this, newSize);
37940 this.fireEvent("moved", this, newSize);
37941 this.fireEvent("resize", this, newSize);
37947 * Get the adapter this SplitBar uses
37948 * @return The adapter object
37950 getAdapter : function(){
37951 return this.adapter;
37955 * Set the adapter this SplitBar uses
37956 * @param {Object} adapter A SplitBar adapter object
37958 setAdapter : function(adapter){
37959 this.adapter = adapter;
37960 this.adapter.init(this);
37964 * Gets the minimum size for the resizing element
37965 * @return {Number} The minimum size
37967 getMinimumSize : function(){
37968 return this.minSize;
37972 * Sets the minimum size for the resizing element
37973 * @param {Number} minSize The minimum size
37975 setMinimumSize : function(minSize){
37976 this.minSize = minSize;
37980 * Gets the maximum size for the resizing element
37981 * @return {Number} The maximum size
37983 getMaximumSize : function(){
37984 return this.maxSize;
37988 * Sets the maximum size for the resizing element
37989 * @param {Number} maxSize The maximum size
37991 setMaximumSize : function(maxSize){
37992 this.maxSize = maxSize;
37996 * Sets the initialize size for the resizing element
37997 * @param {Number} size The initial size
37999 setCurrentSize : function(size){
38000 var oldAnimate = this.animate;
38001 this.animate = false;
38002 this.adapter.setElementSize(this, size);
38003 this.animate = oldAnimate;
38007 * Destroy this splitbar.
38008 * @param {Boolean} removeEl True to remove the element
38010 destroy : function(removeEl){
38012 this.shim.remove();
38015 this.proxy.parentNode.removeChild(this.proxy);
38023 * @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.
38025 Roo.bootstrap.SplitBar.createProxy = function(dir){
38026 var proxy = new Roo.Element(document.createElement("div"));
38027 proxy.unselectable();
38028 var cls = 'roo-splitbar-proxy';
38029 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38030 document.body.appendChild(proxy.dom);
38035 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38036 * Default Adapter. It assumes the splitter and resizing element are not positioned
38037 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38039 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38042 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38043 // do nothing for now
38044 init : function(s){
38048 * Called before drag operations to get the current size of the resizing element.
38049 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38051 getElementSize : function(s){
38052 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38053 return s.resizingEl.getWidth();
38055 return s.resizingEl.getHeight();
38060 * Called after drag operations to set the size of the resizing element.
38061 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38062 * @param {Number} newSize The new size to set
38063 * @param {Function} onComplete A function to be invoked when resizing is complete
38065 setElementSize : function(s, newSize, onComplete){
38066 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38068 s.resizingEl.setWidth(newSize);
38070 onComplete(s, newSize);
38073 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38078 s.resizingEl.setHeight(newSize);
38080 onComplete(s, newSize);
38083 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38090 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38091 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38092 * Adapter that moves the splitter element to align with the resized sizing element.
38093 * Used with an absolute positioned SplitBar.
38094 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38095 * document.body, make sure you assign an id to the body element.
38097 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38098 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38099 this.container = Roo.get(container);
38102 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38103 init : function(s){
38104 this.basic.init(s);
38107 getElementSize : function(s){
38108 return this.basic.getElementSize(s);
38111 setElementSize : function(s, newSize, onComplete){
38112 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38115 moveSplitter : function(s){
38116 var yes = Roo.bootstrap.SplitBar;
38117 switch(s.placement){
38119 s.el.setX(s.resizingEl.getRight());
38122 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38125 s.el.setY(s.resizingEl.getBottom());
38128 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38135 * Orientation constant - Create a vertical SplitBar
38139 Roo.bootstrap.SplitBar.VERTICAL = 1;
38142 * Orientation constant - Create a horizontal SplitBar
38146 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38149 * Placement constant - The resizing element is to the left of the splitter element
38153 Roo.bootstrap.SplitBar.LEFT = 1;
38156 * Placement constant - The resizing element is to the right of the splitter element
38160 Roo.bootstrap.SplitBar.RIGHT = 2;
38163 * Placement constant - The resizing element is positioned above the splitter element
38167 Roo.bootstrap.SplitBar.TOP = 3;
38170 * Placement constant - The resizing element is positioned under splitter element
38174 Roo.bootstrap.SplitBar.BOTTOM = 4;
38175 Roo.namespace("Roo.bootstrap.layout");/*
38177 * Ext JS Library 1.1.1
38178 * Copyright(c) 2006-2007, Ext JS, LLC.
38180 * Originally Released Under LGPL - original licence link has changed is not relivant.
38183 * <script type="text/javascript">
38187 * @class Roo.bootstrap.layout.Manager
38188 * @extends Roo.bootstrap.Component
38189 * Base class for layout managers.
38191 Roo.bootstrap.layout.Manager = function(config)
38193 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38199 /** false to disable window resize monitoring @type Boolean */
38200 this.monitorWindowResize = true;
38205 * Fires when a layout is performed.
38206 * @param {Roo.LayoutManager} this
38210 * @event regionresized
38211 * Fires when the user resizes a region.
38212 * @param {Roo.LayoutRegion} region The resized region
38213 * @param {Number} newSize The new size (width for east/west, height for north/south)
38215 "regionresized" : true,
38217 * @event regioncollapsed
38218 * Fires when a region is collapsed.
38219 * @param {Roo.LayoutRegion} region The collapsed region
38221 "regioncollapsed" : true,
38223 * @event regionexpanded
38224 * Fires when a region is expanded.
38225 * @param {Roo.LayoutRegion} region The expanded region
38227 "regionexpanded" : true
38229 this.updating = false;
38232 this.el = Roo.get(config.el);
38238 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38243 monitorWindowResize : true,
38249 onRender : function(ct, position)
38252 this.el = Roo.get(ct);
38255 //this.fireEvent('render',this);
38259 initEvents: function()
38263 // ie scrollbar fix
38264 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38265 document.body.scroll = "no";
38266 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38267 this.el.position('relative');
38269 this.id = this.el.id;
38270 this.el.addClass("roo-layout-container");
38271 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38272 if(this.el.dom != document.body ) {
38273 this.el.on('resize', this.layout,this);
38274 this.el.on('show', this.layout,this);
38280 * Returns true if this layout is currently being updated
38281 * @return {Boolean}
38283 isUpdating : function(){
38284 return this.updating;
38288 * Suspend the LayoutManager from doing auto-layouts while
38289 * making multiple add or remove calls
38291 beginUpdate : function(){
38292 this.updating = true;
38296 * Restore auto-layouts and optionally disable the manager from performing a layout
38297 * @param {Boolean} noLayout true to disable a layout update
38299 endUpdate : function(noLayout){
38300 this.updating = false;
38306 layout: function(){
38310 onRegionResized : function(region, newSize){
38311 this.fireEvent("regionresized", region, newSize);
38315 onRegionCollapsed : function(region){
38316 this.fireEvent("regioncollapsed", region);
38319 onRegionExpanded : function(region){
38320 this.fireEvent("regionexpanded", region);
38324 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38325 * performs box-model adjustments.
38326 * @return {Object} The size as an object {width: (the width), height: (the height)}
38328 getViewSize : function()
38331 if(this.el.dom != document.body){
38332 size = this.el.getSize();
38334 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38336 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38337 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38342 * Returns the Element this layout is bound to.
38343 * @return {Roo.Element}
38345 getEl : function(){
38350 * Returns the specified region.
38351 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38352 * @return {Roo.LayoutRegion}
38354 getRegion : function(target){
38355 return this.regions[target.toLowerCase()];
38358 onWindowResize : function(){
38359 if(this.monitorWindowResize){
38366 * Ext JS Library 1.1.1
38367 * Copyright(c) 2006-2007, Ext JS, LLC.
38369 * Originally Released Under LGPL - original licence link has changed is not relivant.
38372 * <script type="text/javascript">
38375 * @class Roo.bootstrap.layout.Border
38376 * @extends Roo.bootstrap.layout.Manager
38378 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38379 * please see: examples/bootstrap/nested.html<br><br>
38381 <b>The container the layout is rendered into can be either the body element or any other element.
38382 If it is not the body element, the container needs to either be an absolute positioned element,
38383 or you will need to add "position:relative" to the css of the container. You will also need to specify
38384 the container size if it is not the body element.</b>
38387 * Create a new Border
38388 * @param {Object} config Configuration options
38390 Roo.bootstrap.layout.Border = function(config){
38391 config = config || {};
38392 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38396 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38397 if(config[region]){
38398 config[region].region = region;
38399 this.addRegion(config[region]);
38405 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38407 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38409 parent : false, // this might point to a 'nest' or a ???
38412 * Creates and adds a new region if it doesn't already exist.
38413 * @param {String} target The target region key (north, south, east, west or center).
38414 * @param {Object} config The regions config object
38415 * @return {BorderLayoutRegion} The new region
38417 addRegion : function(config)
38419 if(!this.regions[config.region]){
38420 var r = this.factory(config);
38421 this.bindRegion(r);
38423 return this.regions[config.region];
38427 bindRegion : function(r){
38428 this.regions[r.config.region] = r;
38430 r.on("visibilitychange", this.layout, this);
38431 r.on("paneladded", this.layout, this);
38432 r.on("panelremoved", this.layout, this);
38433 r.on("invalidated", this.layout, this);
38434 r.on("resized", this.onRegionResized, this);
38435 r.on("collapsed", this.onRegionCollapsed, this);
38436 r.on("expanded", this.onRegionExpanded, this);
38440 * Performs a layout update.
38442 layout : function()
38444 if(this.updating) {
38448 // render all the rebions if they have not been done alreayd?
38449 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38450 if(this.regions[region] && !this.regions[region].bodyEl){
38451 this.regions[region].onRender(this.el)
38455 var size = this.getViewSize();
38456 var w = size.width;
38457 var h = size.height;
38462 //var x = 0, y = 0;
38464 var rs = this.regions;
38465 var north = rs["north"];
38466 var south = rs["south"];
38467 var west = rs["west"];
38468 var east = rs["east"];
38469 var center = rs["center"];
38470 //if(this.hideOnLayout){ // not supported anymore
38471 //c.el.setStyle("display", "none");
38473 if(north && north.isVisible()){
38474 var b = north.getBox();
38475 var m = north.getMargins();
38476 b.width = w - (m.left+m.right);
38479 centerY = b.height + b.y + m.bottom;
38480 centerH -= centerY;
38481 north.updateBox(this.safeBox(b));
38483 if(south && south.isVisible()){
38484 var b = south.getBox();
38485 var m = south.getMargins();
38486 b.width = w - (m.left+m.right);
38488 var totalHeight = (b.height + m.top + m.bottom);
38489 b.y = h - totalHeight + m.top;
38490 centerH -= totalHeight;
38491 south.updateBox(this.safeBox(b));
38493 if(west && west.isVisible()){
38494 var b = west.getBox();
38495 var m = west.getMargins();
38496 b.height = centerH - (m.top+m.bottom);
38498 b.y = centerY + m.top;
38499 var totalWidth = (b.width + m.left + m.right);
38500 centerX += totalWidth;
38501 centerW -= totalWidth;
38502 west.updateBox(this.safeBox(b));
38504 if(east && east.isVisible()){
38505 var b = east.getBox();
38506 var m = east.getMargins();
38507 b.height = centerH - (m.top+m.bottom);
38508 var totalWidth = (b.width + m.left + m.right);
38509 b.x = w - totalWidth + m.left;
38510 b.y = centerY + m.top;
38511 centerW -= totalWidth;
38512 east.updateBox(this.safeBox(b));
38515 var m = center.getMargins();
38517 x: centerX + m.left,
38518 y: centerY + m.top,
38519 width: centerW - (m.left+m.right),
38520 height: centerH - (m.top+m.bottom)
38522 //if(this.hideOnLayout){
38523 //center.el.setStyle("display", "block");
38525 center.updateBox(this.safeBox(centerBox));
38528 this.fireEvent("layout", this);
38532 safeBox : function(box){
38533 box.width = Math.max(0, box.width);
38534 box.height = Math.max(0, box.height);
38539 * Adds a ContentPanel (or subclass) to this layout.
38540 * @param {String} target The target region key (north, south, east, west or center).
38541 * @param {Roo.ContentPanel} panel The panel to add
38542 * @return {Roo.ContentPanel} The added panel
38544 add : function(target, panel){
38546 target = target.toLowerCase();
38547 return this.regions[target].add(panel);
38551 * Remove a ContentPanel (or subclass) to this layout.
38552 * @param {String} target The target region key (north, south, east, west or center).
38553 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38554 * @return {Roo.ContentPanel} The removed panel
38556 remove : function(target, panel){
38557 target = target.toLowerCase();
38558 return this.regions[target].remove(panel);
38562 * Searches all regions for a panel with the specified id
38563 * @param {String} panelId
38564 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38566 findPanel : function(panelId){
38567 var rs = this.regions;
38568 for(var target in rs){
38569 if(typeof rs[target] != "function"){
38570 var p = rs[target].getPanel(panelId);
38580 * Searches all regions for a panel with the specified id and activates (shows) it.
38581 * @param {String/ContentPanel} panelId The panels id or the panel itself
38582 * @return {Roo.ContentPanel} The shown panel or null
38584 showPanel : function(panelId) {
38585 var rs = this.regions;
38586 for(var target in rs){
38587 var r = rs[target];
38588 if(typeof r != "function"){
38589 if(r.hasPanel(panelId)){
38590 return r.showPanel(panelId);
38598 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38599 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38602 restoreState : function(provider){
38604 provider = Roo.state.Manager;
38606 var sm = new Roo.LayoutStateManager();
38607 sm.init(this, provider);
38613 * Adds a xtype elements to the layout.
38617 xtype : 'ContentPanel',
38624 xtype : 'NestedLayoutPanel',
38630 items : [ ... list of content panels or nested layout panels.. ]
38634 * @param {Object} cfg Xtype definition of item to add.
38636 addxtype : function(cfg)
38638 // basically accepts a pannel...
38639 // can accept a layout region..!?!?
38640 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38643 // theory? children can only be panels??
38645 //if (!cfg.xtype.match(/Panel$/)) {
38650 if (typeof(cfg.region) == 'undefined') {
38651 Roo.log("Failed to add Panel, region was not set");
38655 var region = cfg.region;
38661 xitems = cfg.items;
38666 if ( region == 'center') {
38667 Roo.log("Center: " + cfg.title);
38673 case 'Content': // ContentPanel (el, cfg)
38674 case 'Scroll': // ContentPanel (el, cfg)
38676 cfg.autoCreate = cfg.autoCreate || true;
38677 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38679 // var el = this.el.createChild();
38680 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38683 this.add(region, ret);
38687 case 'TreePanel': // our new panel!
38688 cfg.el = this.el.createChild();
38689 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38690 this.add(region, ret);
38695 // create a new Layout (which is a Border Layout...
38697 var clayout = cfg.layout;
38698 clayout.el = this.el.createChild();
38699 clayout.items = clayout.items || [];
38703 // replace this exitems with the clayout ones..
38704 xitems = clayout.items;
38706 // force background off if it's in center...
38707 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38708 cfg.background = false;
38710 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38713 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38714 //console.log('adding nested layout panel ' + cfg.toSource());
38715 this.add(region, ret);
38716 nb = {}; /// find first...
38721 // needs grid and region
38723 //var el = this.getRegion(region).el.createChild();
38725 *var el = this.el.createChild();
38726 // create the grid first...
38727 cfg.grid.container = el;
38728 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38731 if (region == 'center' && this.active ) {
38732 cfg.background = false;
38735 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38737 this.add(region, ret);
38739 if (cfg.background) {
38740 // render grid on panel activation (if panel background)
38741 ret.on('activate', function(gp) {
38742 if (!gp.grid.rendered) {
38743 // gp.grid.render(el);
38747 // cfg.grid.render(el);
38753 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38754 // it was the old xcomponent building that caused this before.
38755 // espeically if border is the top element in the tree.
38765 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38767 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38768 this.add(region, ret);
38772 throw "Can not add '" + cfg.xtype + "' to Border";
38778 this.beginUpdate();
38782 Roo.each(xitems, function(i) {
38783 region = nb && i.region ? i.region : false;
38785 var add = ret.addxtype(i);
38788 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38789 if (!i.background) {
38790 abn[region] = nb[region] ;
38797 // make the last non-background panel active..
38798 //if (nb) { Roo.log(abn); }
38801 for(var r in abn) {
38802 region = this.getRegion(r);
38804 // tried using nb[r], but it does not work..
38806 region.showPanel(abn[r]);
38817 factory : function(cfg)
38820 var validRegions = Roo.bootstrap.layout.Border.regions;
38822 var target = cfg.region;
38825 var r = Roo.bootstrap.layout;
38829 return new r.North(cfg);
38831 return new r.South(cfg);
38833 return new r.East(cfg);
38835 return new r.West(cfg);
38837 return new r.Center(cfg);
38839 throw 'Layout region "'+target+'" not supported.';
38846 * Ext JS Library 1.1.1
38847 * Copyright(c) 2006-2007, Ext JS, LLC.
38849 * Originally Released Under LGPL - original licence link has changed is not relivant.
38852 * <script type="text/javascript">
38856 * @class Roo.bootstrap.layout.Basic
38857 * @extends Roo.util.Observable
38858 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38859 * and does not have a titlebar, tabs or any other features. All it does is size and position
38860 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38861 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38862 * @cfg {string} region the region that it inhabits..
38863 * @cfg {bool} skipConfig skip config?
38867 Roo.bootstrap.layout.Basic = function(config){
38869 this.mgr = config.mgr;
38871 this.position = config.region;
38873 var skipConfig = config.skipConfig;
38877 * @scope Roo.BasicLayoutRegion
38881 * @event beforeremove
38882 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38883 * @param {Roo.LayoutRegion} this
38884 * @param {Roo.ContentPanel} panel The panel
38885 * @param {Object} e The cancel event object
38887 "beforeremove" : true,
38889 * @event invalidated
38890 * Fires when the layout for this region is changed.
38891 * @param {Roo.LayoutRegion} this
38893 "invalidated" : true,
38895 * @event visibilitychange
38896 * Fires when this region is shown or hidden
38897 * @param {Roo.LayoutRegion} this
38898 * @param {Boolean} visibility true or false
38900 "visibilitychange" : true,
38902 * @event paneladded
38903 * Fires when a panel is added.
38904 * @param {Roo.LayoutRegion} this
38905 * @param {Roo.ContentPanel} panel The panel
38907 "paneladded" : true,
38909 * @event panelremoved
38910 * Fires when a panel is removed.
38911 * @param {Roo.LayoutRegion} this
38912 * @param {Roo.ContentPanel} panel The panel
38914 "panelremoved" : true,
38916 * @event beforecollapse
38917 * Fires when this region before collapse.
38918 * @param {Roo.LayoutRegion} this
38920 "beforecollapse" : true,
38923 * Fires when this region is collapsed.
38924 * @param {Roo.LayoutRegion} this
38926 "collapsed" : true,
38929 * Fires when this region is expanded.
38930 * @param {Roo.LayoutRegion} this
38935 * Fires when this region is slid into view.
38936 * @param {Roo.LayoutRegion} this
38938 "slideshow" : true,
38941 * Fires when this region slides out of view.
38942 * @param {Roo.LayoutRegion} this
38944 "slidehide" : true,
38946 * @event panelactivated
38947 * Fires when a panel is activated.
38948 * @param {Roo.LayoutRegion} this
38949 * @param {Roo.ContentPanel} panel The activated panel
38951 "panelactivated" : true,
38954 * Fires when the user resizes this region.
38955 * @param {Roo.LayoutRegion} this
38956 * @param {Number} newSize The new size (width for east/west, height for north/south)
38960 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38961 this.panels = new Roo.util.MixedCollection();
38962 this.panels.getKey = this.getPanelId.createDelegate(this);
38964 this.activePanel = null;
38965 // ensure listeners are added...
38967 if (config.listeners || config.events) {
38968 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38969 listeners : config.listeners || {},
38970 events : config.events || {}
38974 if(skipConfig !== true){
38975 this.applyConfig(config);
38979 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38981 getPanelId : function(p){
38985 applyConfig : function(config){
38986 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38987 this.config = config;
38992 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38993 * the width, for horizontal (north, south) the height.
38994 * @param {Number} newSize The new width or height
38996 resizeTo : function(newSize){
38997 var el = this.el ? this.el :
38998 (this.activePanel ? this.activePanel.getEl() : null);
39000 switch(this.position){
39003 el.setWidth(newSize);
39004 this.fireEvent("resized", this, newSize);
39008 el.setHeight(newSize);
39009 this.fireEvent("resized", this, newSize);
39015 getBox : function(){
39016 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39019 getMargins : function(){
39020 return this.margins;
39023 updateBox : function(box){
39025 var el = this.activePanel.getEl();
39026 el.dom.style.left = box.x + "px";
39027 el.dom.style.top = box.y + "px";
39028 this.activePanel.setSize(box.width, box.height);
39032 * Returns the container element for this region.
39033 * @return {Roo.Element}
39035 getEl : function(){
39036 return this.activePanel;
39040 * Returns true if this region is currently visible.
39041 * @return {Boolean}
39043 isVisible : function(){
39044 return this.activePanel ? true : false;
39047 setActivePanel : function(panel){
39048 panel = this.getPanel(panel);
39049 if(this.activePanel && this.activePanel != panel){
39050 this.activePanel.setActiveState(false);
39051 this.activePanel.getEl().setLeftTop(-10000,-10000);
39053 this.activePanel = panel;
39054 panel.setActiveState(true);
39056 panel.setSize(this.box.width, this.box.height);
39058 this.fireEvent("panelactivated", this, panel);
39059 this.fireEvent("invalidated");
39063 * Show the specified panel.
39064 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39065 * @return {Roo.ContentPanel} The shown panel or null
39067 showPanel : function(panel){
39068 panel = this.getPanel(panel);
39070 this.setActivePanel(panel);
39076 * Get the active panel for this region.
39077 * @return {Roo.ContentPanel} The active panel or null
39079 getActivePanel : function(){
39080 return this.activePanel;
39084 * Add the passed ContentPanel(s)
39085 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39086 * @return {Roo.ContentPanel} The panel added (if only one was added)
39088 add : function(panel){
39089 if(arguments.length > 1){
39090 for(var i = 0, len = arguments.length; i < len; i++) {
39091 this.add(arguments[i]);
39095 if(this.hasPanel(panel)){
39096 this.showPanel(panel);
39099 var el = panel.getEl();
39100 if(el.dom.parentNode != this.mgr.el.dom){
39101 this.mgr.el.dom.appendChild(el.dom);
39103 if(panel.setRegion){
39104 panel.setRegion(this);
39106 this.panels.add(panel);
39107 el.setStyle("position", "absolute");
39108 if(!panel.background){
39109 this.setActivePanel(panel);
39110 if(this.config.initialSize && this.panels.getCount()==1){
39111 this.resizeTo(this.config.initialSize);
39114 this.fireEvent("paneladded", this, panel);
39119 * Returns true if the panel is in this region.
39120 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39121 * @return {Boolean}
39123 hasPanel : function(panel){
39124 if(typeof panel == "object"){ // must be panel obj
39125 panel = panel.getId();
39127 return this.getPanel(panel) ? true : false;
39131 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39132 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39133 * @param {Boolean} preservePanel Overrides the config preservePanel option
39134 * @return {Roo.ContentPanel} The panel that was removed
39136 remove : function(panel, preservePanel){
39137 panel = this.getPanel(panel);
39142 this.fireEvent("beforeremove", this, panel, e);
39143 if(e.cancel === true){
39146 var panelId = panel.getId();
39147 this.panels.removeKey(panelId);
39152 * Returns the panel specified or null if it's not in this region.
39153 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39154 * @return {Roo.ContentPanel}
39156 getPanel : function(id){
39157 if(typeof id == "object"){ // must be panel obj
39160 return this.panels.get(id);
39164 * Returns this regions position (north/south/east/west/center).
39167 getPosition: function(){
39168 return this.position;
39172 * Ext JS Library 1.1.1
39173 * Copyright(c) 2006-2007, Ext JS, LLC.
39175 * Originally Released Under LGPL - original licence link has changed is not relivant.
39178 * <script type="text/javascript">
39182 * @class Roo.bootstrap.layout.Region
39183 * @extends Roo.bootstrap.layout.Basic
39184 * This class represents a region in a layout manager.
39186 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39187 * @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})
39188 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39189 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39190 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39191 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39192 * @cfg {String} title The title for the region (overrides panel titles)
39193 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39194 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39195 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39196 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39197 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39198 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39199 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39200 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39201 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39202 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39204 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39205 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39206 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39207 * @cfg {Number} width For East/West panels
39208 * @cfg {Number} height For North/South panels
39209 * @cfg {Boolean} split To show the splitter
39210 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39212 * @cfg {string} cls Extra CSS classes to add to region
39214 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39215 * @cfg {string} region the region that it inhabits..
39218 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39219 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39221 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39222 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39223 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39225 Roo.bootstrap.layout.Region = function(config)
39227 this.applyConfig(config);
39229 var mgr = config.mgr;
39230 var pos = config.region;
39231 config.skipConfig = true;
39232 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39235 this.onRender(mgr.el);
39238 this.visible = true;
39239 this.collapsed = false;
39240 this.unrendered_panels = [];
39243 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39245 position: '', // set by wrapper (eg. north/south etc..)
39246 unrendered_panels : null, // unrendered panels.
39248 tabPosition : false,
39250 mgr: false, // points to 'Border'
39253 createBody : function(){
39254 /** This region's body element
39255 * @type Roo.Element */
39256 this.bodyEl = this.el.createChild({
39258 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39262 onRender: function(ctr, pos)
39264 var dh = Roo.DomHelper;
39265 /** This region's container element
39266 * @type Roo.Element */
39267 this.el = dh.append(ctr.dom, {
39269 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39271 /** This region's title element
39272 * @type Roo.Element */
39274 this.titleEl = dh.append(this.el.dom, {
39276 unselectable: "on",
39277 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39279 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39280 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39284 this.titleEl.enableDisplayMode();
39285 /** This region's title text element
39286 * @type HTMLElement */
39287 this.titleTextEl = this.titleEl.dom.firstChild;
39288 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39290 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39291 this.closeBtn.enableDisplayMode();
39292 this.closeBtn.on("click", this.closeClicked, this);
39293 this.closeBtn.hide();
39295 this.createBody(this.config);
39296 if(this.config.hideWhenEmpty){
39298 this.on("paneladded", this.validateVisibility, this);
39299 this.on("panelremoved", this.validateVisibility, this);
39301 if(this.autoScroll){
39302 this.bodyEl.setStyle("overflow", "auto");
39304 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39306 //if(c.titlebar !== false){
39307 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39308 this.titleEl.hide();
39310 this.titleEl.show();
39311 if(this.config.title){
39312 this.titleTextEl.innerHTML = this.config.title;
39316 if(this.config.collapsed){
39317 this.collapse(true);
39319 if(this.config.hidden){
39323 if (this.unrendered_panels && this.unrendered_panels.length) {
39324 for (var i =0;i< this.unrendered_panels.length; i++) {
39325 this.add(this.unrendered_panels[i]);
39327 this.unrendered_panels = null;
39333 applyConfig : function(c)
39336 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39337 var dh = Roo.DomHelper;
39338 if(c.titlebar !== false){
39339 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39340 this.collapseBtn.on("click", this.collapse, this);
39341 this.collapseBtn.enableDisplayMode();
39343 if(c.showPin === true || this.showPin){
39344 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39345 this.stickBtn.enableDisplayMode();
39346 this.stickBtn.on("click", this.expand, this);
39347 this.stickBtn.hide();
39352 /** This region's collapsed element
39353 * @type Roo.Element */
39356 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39357 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39360 if(c.floatable !== false){
39361 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39362 this.collapsedEl.on("click", this.collapseClick, this);
39365 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39366 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39367 id: "message", unselectable: "on", style:{"float":"left"}});
39368 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39370 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39371 this.expandBtn.on("click", this.expand, this);
39375 if(this.collapseBtn){
39376 this.collapseBtn.setVisible(c.collapsible == true);
39379 this.cmargins = c.cmargins || this.cmargins ||
39380 (this.position == "west" || this.position == "east" ?
39381 {top: 0, left: 2, right:2, bottom: 0} :
39382 {top: 2, left: 0, right:0, bottom: 2});
39384 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39387 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39389 this.autoScroll = c.autoScroll || false;
39394 this.duration = c.duration || .30;
39395 this.slideDuration = c.slideDuration || .45;
39400 * Returns true if this region is currently visible.
39401 * @return {Boolean}
39403 isVisible : function(){
39404 return this.visible;
39408 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39409 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39411 //setCollapsedTitle : function(title){
39412 // title = title || " ";
39413 // if(this.collapsedTitleTextEl){
39414 // this.collapsedTitleTextEl.innerHTML = title;
39418 getBox : function(){
39420 // if(!this.collapsed){
39421 b = this.el.getBox(false, true);
39423 // b = this.collapsedEl.getBox(false, true);
39428 getMargins : function(){
39429 return this.margins;
39430 //return this.collapsed ? this.cmargins : this.margins;
39433 highlight : function(){
39434 this.el.addClass("x-layout-panel-dragover");
39437 unhighlight : function(){
39438 this.el.removeClass("x-layout-panel-dragover");
39441 updateBox : function(box)
39443 if (!this.bodyEl) {
39444 return; // not rendered yet..
39448 if(!this.collapsed){
39449 this.el.dom.style.left = box.x + "px";
39450 this.el.dom.style.top = box.y + "px";
39451 this.updateBody(box.width, box.height);
39453 this.collapsedEl.dom.style.left = box.x + "px";
39454 this.collapsedEl.dom.style.top = box.y + "px";
39455 this.collapsedEl.setSize(box.width, box.height);
39458 this.tabs.autoSizeTabs();
39462 updateBody : function(w, h)
39465 this.el.setWidth(w);
39466 w -= this.el.getBorderWidth("rl");
39467 if(this.config.adjustments){
39468 w += this.config.adjustments[0];
39471 if(h !== null && h > 0){
39472 this.el.setHeight(h);
39473 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39474 h -= this.el.getBorderWidth("tb");
39475 if(this.config.adjustments){
39476 h += this.config.adjustments[1];
39478 this.bodyEl.setHeight(h);
39480 h = this.tabs.syncHeight(h);
39483 if(this.panelSize){
39484 w = w !== null ? w : this.panelSize.width;
39485 h = h !== null ? h : this.panelSize.height;
39487 if(this.activePanel){
39488 var el = this.activePanel.getEl();
39489 w = w !== null ? w : el.getWidth();
39490 h = h !== null ? h : el.getHeight();
39491 this.panelSize = {width: w, height: h};
39492 this.activePanel.setSize(w, h);
39494 if(Roo.isIE && this.tabs){
39495 this.tabs.el.repaint();
39500 * Returns the container element for this region.
39501 * @return {Roo.Element}
39503 getEl : function(){
39508 * Hides this region.
39511 //if(!this.collapsed){
39512 this.el.dom.style.left = "-2000px";
39515 // this.collapsedEl.dom.style.left = "-2000px";
39516 // this.collapsedEl.hide();
39518 this.visible = false;
39519 this.fireEvent("visibilitychange", this, false);
39523 * Shows this region if it was previously hidden.
39526 //if(!this.collapsed){
39529 // this.collapsedEl.show();
39531 this.visible = true;
39532 this.fireEvent("visibilitychange", this, true);
39535 closeClicked : function(){
39536 if(this.activePanel){
39537 this.remove(this.activePanel);
39541 collapseClick : function(e){
39543 e.stopPropagation();
39546 e.stopPropagation();
39552 * Collapses this region.
39553 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39556 collapse : function(skipAnim, skipCheck = false){
39557 if(this.collapsed) {
39561 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39563 this.collapsed = true;
39565 this.split.el.hide();
39567 if(this.config.animate && skipAnim !== true){
39568 this.fireEvent("invalidated", this);
39569 this.animateCollapse();
39571 this.el.setLocation(-20000,-20000);
39573 this.collapsedEl.show();
39574 this.fireEvent("collapsed", this);
39575 this.fireEvent("invalidated", this);
39581 animateCollapse : function(){
39586 * Expands this region if it was previously collapsed.
39587 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39588 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39591 expand : function(e, skipAnim){
39593 e.stopPropagation();
39595 if(!this.collapsed || this.el.hasActiveFx()) {
39599 this.afterSlideIn();
39602 this.collapsed = false;
39603 if(this.config.animate && skipAnim !== true){
39604 this.animateExpand();
39608 this.split.el.show();
39610 this.collapsedEl.setLocation(-2000,-2000);
39611 this.collapsedEl.hide();
39612 this.fireEvent("invalidated", this);
39613 this.fireEvent("expanded", this);
39617 animateExpand : function(){
39621 initTabs : function()
39623 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39625 var ts = new Roo.bootstrap.panel.Tabs({
39626 el: this.bodyEl.dom,
39628 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39629 disableTooltips: this.config.disableTabTips,
39630 toolbar : this.config.toolbar
39633 if(this.config.hideTabs){
39634 ts.stripWrap.setDisplayed(false);
39637 ts.resizeTabs = this.config.resizeTabs === true;
39638 ts.minTabWidth = this.config.minTabWidth || 40;
39639 ts.maxTabWidth = this.config.maxTabWidth || 250;
39640 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39641 ts.monitorResize = false;
39642 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39643 ts.bodyEl.addClass('roo-layout-tabs-body');
39644 this.panels.each(this.initPanelAsTab, this);
39647 initPanelAsTab : function(panel){
39648 var ti = this.tabs.addTab(
39652 this.config.closeOnTab && panel.isClosable(),
39655 if(panel.tabTip !== undefined){
39656 ti.setTooltip(panel.tabTip);
39658 ti.on("activate", function(){
39659 this.setActivePanel(panel);
39662 if(this.config.closeOnTab){
39663 ti.on("beforeclose", function(t, e){
39665 this.remove(panel);
39669 panel.tabItem = ti;
39674 updatePanelTitle : function(panel, title)
39676 if(this.activePanel == panel){
39677 this.updateTitle(title);
39680 var ti = this.tabs.getTab(panel.getEl().id);
39682 if(panel.tabTip !== undefined){
39683 ti.setTooltip(panel.tabTip);
39688 updateTitle : function(title){
39689 if(this.titleTextEl && !this.config.title){
39690 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39694 setActivePanel : function(panel)
39696 panel = this.getPanel(panel);
39697 if(this.activePanel && this.activePanel != panel){
39698 if(this.activePanel.setActiveState(false) === false){
39702 this.activePanel = panel;
39703 panel.setActiveState(true);
39704 if(this.panelSize){
39705 panel.setSize(this.panelSize.width, this.panelSize.height);
39708 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39710 this.updateTitle(panel.getTitle());
39712 this.fireEvent("invalidated", this);
39714 this.fireEvent("panelactivated", this, panel);
39718 * Shows the specified panel.
39719 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39720 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39722 showPanel : function(panel)
39724 panel = this.getPanel(panel);
39727 var tab = this.tabs.getTab(panel.getEl().id);
39728 if(tab.isHidden()){
39729 this.tabs.unhideTab(tab.id);
39733 this.setActivePanel(panel);
39740 * Get the active panel for this region.
39741 * @return {Roo.ContentPanel} The active panel or null
39743 getActivePanel : function(){
39744 return this.activePanel;
39747 validateVisibility : function(){
39748 if(this.panels.getCount() < 1){
39749 this.updateTitle(" ");
39750 this.closeBtn.hide();
39753 if(!this.isVisible()){
39760 * Adds the passed ContentPanel(s) to this region.
39761 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39762 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39764 add : function(panel)
39766 if(arguments.length > 1){
39767 for(var i = 0, len = arguments.length; i < len; i++) {
39768 this.add(arguments[i]);
39773 // if we have not been rendered yet, then we can not really do much of this..
39774 if (!this.bodyEl) {
39775 this.unrendered_panels.push(panel);
39782 if(this.hasPanel(panel)){
39783 this.showPanel(panel);
39786 panel.setRegion(this);
39787 this.panels.add(panel);
39788 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39789 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39790 // and hide them... ???
39791 this.bodyEl.dom.appendChild(panel.getEl().dom);
39792 if(panel.background !== true){
39793 this.setActivePanel(panel);
39795 this.fireEvent("paneladded", this, panel);
39802 this.initPanelAsTab(panel);
39806 if(panel.background !== true){
39807 this.tabs.activate(panel.getEl().id);
39809 this.fireEvent("paneladded", this, panel);
39814 * Hides the tab for the specified panel.
39815 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39817 hidePanel : function(panel){
39818 if(this.tabs && (panel = this.getPanel(panel))){
39819 this.tabs.hideTab(panel.getEl().id);
39824 * Unhides the tab for a previously hidden panel.
39825 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39827 unhidePanel : function(panel){
39828 if(this.tabs && (panel = this.getPanel(panel))){
39829 this.tabs.unhideTab(panel.getEl().id);
39833 clearPanels : function(){
39834 while(this.panels.getCount() > 0){
39835 this.remove(this.panels.first());
39840 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39841 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39842 * @param {Boolean} preservePanel Overrides the config preservePanel option
39843 * @return {Roo.ContentPanel} The panel that was removed
39845 remove : function(panel, preservePanel)
39847 panel = this.getPanel(panel);
39852 this.fireEvent("beforeremove", this, panel, e);
39853 if(e.cancel === true){
39856 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39857 var panelId = panel.getId();
39858 this.panels.removeKey(panelId);
39860 document.body.appendChild(panel.getEl().dom);
39863 this.tabs.removeTab(panel.getEl().id);
39864 }else if (!preservePanel){
39865 this.bodyEl.dom.removeChild(panel.getEl().dom);
39867 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39868 var p = this.panels.first();
39869 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39870 tempEl.appendChild(p.getEl().dom);
39871 this.bodyEl.update("");
39872 this.bodyEl.dom.appendChild(p.getEl().dom);
39874 this.updateTitle(p.getTitle());
39876 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39877 this.setActivePanel(p);
39879 panel.setRegion(null);
39880 if(this.activePanel == panel){
39881 this.activePanel = null;
39883 if(this.config.autoDestroy !== false && preservePanel !== true){
39884 try{panel.destroy();}catch(e){}
39886 this.fireEvent("panelremoved", this, panel);
39891 * Returns the TabPanel component used by this region
39892 * @return {Roo.TabPanel}
39894 getTabs : function(){
39898 createTool : function(parentEl, className){
39899 var btn = Roo.DomHelper.append(parentEl, {
39901 cls: "x-layout-tools-button",
39904 cls: "roo-layout-tools-button-inner " + className,
39908 btn.addClassOnOver("roo-layout-tools-button-over");
39913 * Ext JS Library 1.1.1
39914 * Copyright(c) 2006-2007, Ext JS, LLC.
39916 * Originally Released Under LGPL - original licence link has changed is not relivant.
39919 * <script type="text/javascript">
39925 * @class Roo.SplitLayoutRegion
39926 * @extends Roo.LayoutRegion
39927 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39929 Roo.bootstrap.layout.Split = function(config){
39930 this.cursor = config.cursor;
39931 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39934 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39936 splitTip : "Drag to resize.",
39937 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39938 useSplitTips : false,
39940 applyConfig : function(config){
39941 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39944 onRender : function(ctr,pos) {
39946 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39947 if(!this.config.split){
39952 var splitEl = Roo.DomHelper.append(ctr.dom, {
39954 id: this.el.id + "-split",
39955 cls: "roo-layout-split roo-layout-split-"+this.position,
39958 /** The SplitBar for this region
39959 * @type Roo.SplitBar */
39960 // does not exist yet...
39961 Roo.log([this.position, this.orientation]);
39963 this.split = new Roo.bootstrap.SplitBar({
39964 dragElement : splitEl,
39965 resizingElement: this.el,
39966 orientation : this.orientation
39969 this.split.on("moved", this.onSplitMove, this);
39970 this.split.useShim = this.config.useShim === true;
39971 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39972 if(this.useSplitTips){
39973 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39975 //if(config.collapsible){
39976 // this.split.el.on("dblclick", this.collapse, this);
39979 if(typeof this.config.minSize != "undefined"){
39980 this.split.minSize = this.config.minSize;
39982 if(typeof this.config.maxSize != "undefined"){
39983 this.split.maxSize = this.config.maxSize;
39985 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39986 this.hideSplitter();
39991 getHMaxSize : function(){
39992 var cmax = this.config.maxSize || 10000;
39993 var center = this.mgr.getRegion("center");
39994 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39997 getVMaxSize : function(){
39998 var cmax = this.config.maxSize || 10000;
39999 var center = this.mgr.getRegion("center");
40000 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40003 onSplitMove : function(split, newSize){
40004 this.fireEvent("resized", this, newSize);
40008 * Returns the {@link Roo.SplitBar} for this region.
40009 * @return {Roo.SplitBar}
40011 getSplitBar : function(){
40016 this.hideSplitter();
40017 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40020 hideSplitter : function(){
40022 this.split.el.setLocation(-2000,-2000);
40023 this.split.el.hide();
40029 this.split.el.show();
40031 Roo.bootstrap.layout.Split.superclass.show.call(this);
40034 beforeSlide: function(){
40035 if(Roo.isGecko){// firefox overflow auto bug workaround
40036 this.bodyEl.clip();
40038 this.tabs.bodyEl.clip();
40040 if(this.activePanel){
40041 this.activePanel.getEl().clip();
40043 if(this.activePanel.beforeSlide){
40044 this.activePanel.beforeSlide();
40050 afterSlide : function(){
40051 if(Roo.isGecko){// firefox overflow auto bug workaround
40052 this.bodyEl.unclip();
40054 this.tabs.bodyEl.unclip();
40056 if(this.activePanel){
40057 this.activePanel.getEl().unclip();
40058 if(this.activePanel.afterSlide){
40059 this.activePanel.afterSlide();
40065 initAutoHide : function(){
40066 if(this.autoHide !== false){
40067 if(!this.autoHideHd){
40068 var st = new Roo.util.DelayedTask(this.slideIn, this);
40069 this.autoHideHd = {
40070 "mouseout": function(e){
40071 if(!e.within(this.el, true)){
40075 "mouseover" : function(e){
40081 this.el.on(this.autoHideHd);
40085 clearAutoHide : function(){
40086 if(this.autoHide !== false){
40087 this.el.un("mouseout", this.autoHideHd.mouseout);
40088 this.el.un("mouseover", this.autoHideHd.mouseover);
40092 clearMonitor : function(){
40093 Roo.get(document).un("click", this.slideInIf, this);
40096 // these names are backwards but not changed for compat
40097 slideOut : function(){
40098 if(this.isSlid || this.el.hasActiveFx()){
40101 this.isSlid = true;
40102 if(this.collapseBtn){
40103 this.collapseBtn.hide();
40105 this.closeBtnState = this.closeBtn.getStyle('display');
40106 this.closeBtn.hide();
40108 this.stickBtn.show();
40111 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40112 this.beforeSlide();
40113 this.el.setStyle("z-index", 10001);
40114 this.el.slideIn(this.getSlideAnchor(), {
40115 callback: function(){
40117 this.initAutoHide();
40118 Roo.get(document).on("click", this.slideInIf, this);
40119 this.fireEvent("slideshow", this);
40126 afterSlideIn : function(){
40127 this.clearAutoHide();
40128 this.isSlid = false;
40129 this.clearMonitor();
40130 this.el.setStyle("z-index", "");
40131 if(this.collapseBtn){
40132 this.collapseBtn.show();
40134 this.closeBtn.setStyle('display', this.closeBtnState);
40136 this.stickBtn.hide();
40138 this.fireEvent("slidehide", this);
40141 slideIn : function(cb){
40142 if(!this.isSlid || this.el.hasActiveFx()){
40146 this.isSlid = false;
40147 this.beforeSlide();
40148 this.el.slideOut(this.getSlideAnchor(), {
40149 callback: function(){
40150 this.el.setLeftTop(-10000, -10000);
40152 this.afterSlideIn();
40160 slideInIf : function(e){
40161 if(!e.within(this.el)){
40166 animateCollapse : function(){
40167 this.beforeSlide();
40168 this.el.setStyle("z-index", 20000);
40169 var anchor = this.getSlideAnchor();
40170 this.el.slideOut(anchor, {
40171 callback : function(){
40172 this.el.setStyle("z-index", "");
40173 this.collapsedEl.slideIn(anchor, {duration:.3});
40175 this.el.setLocation(-10000,-10000);
40177 this.fireEvent("collapsed", this);
40184 animateExpand : function(){
40185 this.beforeSlide();
40186 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40187 this.el.setStyle("z-index", 20000);
40188 this.collapsedEl.hide({
40191 this.el.slideIn(this.getSlideAnchor(), {
40192 callback : function(){
40193 this.el.setStyle("z-index", "");
40196 this.split.el.show();
40198 this.fireEvent("invalidated", this);
40199 this.fireEvent("expanded", this);
40227 getAnchor : function(){
40228 return this.anchors[this.position];
40231 getCollapseAnchor : function(){
40232 return this.canchors[this.position];
40235 getSlideAnchor : function(){
40236 return this.sanchors[this.position];
40239 getAlignAdj : function(){
40240 var cm = this.cmargins;
40241 switch(this.position){
40257 getExpandAdj : function(){
40258 var c = this.collapsedEl, cm = this.cmargins;
40259 switch(this.position){
40261 return [-(cm.right+c.getWidth()+cm.left), 0];
40264 return [cm.right+c.getWidth()+cm.left, 0];
40267 return [0, -(cm.top+cm.bottom+c.getHeight())];
40270 return [0, cm.top+cm.bottom+c.getHeight()];
40276 * Ext JS Library 1.1.1
40277 * Copyright(c) 2006-2007, Ext JS, LLC.
40279 * Originally Released Under LGPL - original licence link has changed is not relivant.
40282 * <script type="text/javascript">
40285 * These classes are private internal classes
40287 Roo.bootstrap.layout.Center = function(config){
40288 config.region = "center";
40289 Roo.bootstrap.layout.Region.call(this, config);
40290 this.visible = true;
40291 this.minWidth = config.minWidth || 20;
40292 this.minHeight = config.minHeight || 20;
40295 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40297 // center panel can't be hidden
40301 // center panel can't be hidden
40304 getMinWidth: function(){
40305 return this.minWidth;
40308 getMinHeight: function(){
40309 return this.minHeight;
40323 Roo.bootstrap.layout.North = function(config)
40325 config.region = 'north';
40326 config.cursor = 'n-resize';
40328 Roo.bootstrap.layout.Split.call(this, config);
40332 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40333 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40334 this.split.el.addClass("roo-layout-split-v");
40336 //var size = config.initialSize || config.height;
40337 //if(this.el && typeof size != "undefined"){
40338 // this.el.setHeight(size);
40341 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40343 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40346 onRender : function(ctr, pos)
40348 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40349 var size = this.config.initialSize || this.config.height;
40350 if(this.el && typeof size != "undefined"){
40351 this.el.setHeight(size);
40356 getBox : function(){
40357 if(this.collapsed){
40358 return this.collapsedEl.getBox();
40360 var box = this.el.getBox();
40362 box.height += this.split.el.getHeight();
40367 updateBox : function(box){
40368 if(this.split && !this.collapsed){
40369 box.height -= this.split.el.getHeight();
40370 this.split.el.setLeft(box.x);
40371 this.split.el.setTop(box.y+box.height);
40372 this.split.el.setWidth(box.width);
40374 if(this.collapsed){
40375 this.updateBody(box.width, null);
40377 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40385 Roo.bootstrap.layout.South = function(config){
40386 config.region = 'south';
40387 config.cursor = 's-resize';
40388 Roo.bootstrap.layout.Split.call(this, config);
40390 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40391 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40392 this.split.el.addClass("roo-layout-split-v");
40397 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40398 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40400 onRender : function(ctr, pos)
40402 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40403 var size = this.config.initialSize || this.config.height;
40404 if(this.el && typeof size != "undefined"){
40405 this.el.setHeight(size);
40410 getBox : function(){
40411 if(this.collapsed){
40412 return this.collapsedEl.getBox();
40414 var box = this.el.getBox();
40416 var sh = this.split.el.getHeight();
40423 updateBox : function(box){
40424 if(this.split && !this.collapsed){
40425 var sh = this.split.el.getHeight();
40428 this.split.el.setLeft(box.x);
40429 this.split.el.setTop(box.y-sh);
40430 this.split.el.setWidth(box.width);
40432 if(this.collapsed){
40433 this.updateBody(box.width, null);
40435 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40439 Roo.bootstrap.layout.East = function(config){
40440 config.region = "east";
40441 config.cursor = "e-resize";
40442 Roo.bootstrap.layout.Split.call(this, config);
40444 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40445 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40446 this.split.el.addClass("roo-layout-split-h");
40450 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40451 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40453 onRender : function(ctr, pos)
40455 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40456 var size = this.config.initialSize || this.config.width;
40457 if(this.el && typeof size != "undefined"){
40458 this.el.setWidth(size);
40463 getBox : function(){
40464 if(this.collapsed){
40465 return this.collapsedEl.getBox();
40467 var box = this.el.getBox();
40469 var sw = this.split.el.getWidth();
40476 updateBox : function(box){
40477 if(this.split && !this.collapsed){
40478 var sw = this.split.el.getWidth();
40480 this.split.el.setLeft(box.x);
40481 this.split.el.setTop(box.y);
40482 this.split.el.setHeight(box.height);
40485 if(this.collapsed){
40486 this.updateBody(null, box.height);
40488 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40492 Roo.bootstrap.layout.West = function(config){
40493 config.region = "west";
40494 config.cursor = "w-resize";
40496 Roo.bootstrap.layout.Split.call(this, config);
40498 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40499 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40500 this.split.el.addClass("roo-layout-split-h");
40504 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40505 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40507 onRender: function(ctr, pos)
40509 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40510 var size = this.config.initialSize || this.config.width;
40511 if(typeof size != "undefined"){
40512 this.el.setWidth(size);
40516 getBox : function(){
40517 if(this.collapsed){
40518 return this.collapsedEl.getBox();
40520 var box = this.el.getBox();
40521 if (box.width == 0) {
40522 box.width = this.config.width; // kludge?
40525 box.width += this.split.el.getWidth();
40530 updateBox : function(box){
40531 if(this.split && !this.collapsed){
40532 var sw = this.split.el.getWidth();
40534 this.split.el.setLeft(box.x+box.width);
40535 this.split.el.setTop(box.y);
40536 this.split.el.setHeight(box.height);
40538 if(this.collapsed){
40539 this.updateBody(null, box.height);
40541 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40543 });Roo.namespace("Roo.bootstrap.panel");/*
40545 * Ext JS Library 1.1.1
40546 * Copyright(c) 2006-2007, Ext JS, LLC.
40548 * Originally Released Under LGPL - original licence link has changed is not relivant.
40551 * <script type="text/javascript">
40554 * @class Roo.ContentPanel
40555 * @extends Roo.util.Observable
40557 * A basic ContentPanel element.
40558 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40559 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40560 * @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
40561 * @cfg {Boolean} closable True if the panel can be closed/removed
40562 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40563 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40564 * @cfg {Toolbar} toolbar A toolbar for this panel
40565 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40566 * @cfg {String} title The title for this panel
40567 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40568 * @cfg {String} url Calls {@link #setUrl} with this value
40569 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40570 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40571 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40572 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40573 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40574 * @cfg {Boolean} badges render the badges
40575 * @cfg {String} cls extra classes to use
40576 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40579 * Create a new ContentPanel.
40580 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40581 * @param {String/Object} config A string to set only the title or a config object
40582 * @param {String} content (optional) Set the HTML content for this panel
40583 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40585 Roo.bootstrap.panel.Content = function( config){
40587 this.tpl = config.tpl || false;
40589 var el = config.el;
40590 var content = config.content;
40592 if(config.autoCreate){ // xtype is available if this is called from factory
40595 this.el = Roo.get(el);
40596 if(!this.el && config && config.autoCreate){
40597 if(typeof config.autoCreate == "object"){
40598 if(!config.autoCreate.id){
40599 config.autoCreate.id = config.id||el;
40601 this.el = Roo.DomHelper.append(document.body,
40602 config.autoCreate, true);
40606 cls: (config.cls || '') +
40607 (config.background ? ' bg-' + config.background : '') +
40608 " roo-layout-inactive-content",
40611 if (config.iframe) {
40615 style : 'border: 0px',
40616 src : 'about:blank'
40622 elcfg.html = config.html;
40626 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40627 if (config.iframe) {
40628 this.iframeEl = this.el.select('iframe',true).first();
40633 this.closable = false;
40634 this.loaded = false;
40635 this.active = false;
40638 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40640 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40642 this.wrapEl = this.el; //this.el.wrap();
40644 if (config.toolbar.items) {
40645 ti = config.toolbar.items ;
40646 delete config.toolbar.items ;
40650 this.toolbar.render(this.wrapEl, 'before');
40651 for(var i =0;i < ti.length;i++) {
40652 // Roo.log(['add child', items[i]]);
40653 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40655 this.toolbar.items = nitems;
40656 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40657 delete config.toolbar;
40661 // xtype created footer. - not sure if will work as we normally have to render first..
40662 if (this.footer && !this.footer.el && this.footer.xtype) {
40663 if (!this.wrapEl) {
40664 this.wrapEl = this.el.wrap();
40667 this.footer.container = this.wrapEl.createChild();
40669 this.footer = Roo.factory(this.footer, Roo);
40674 if(typeof config == "string"){
40675 this.title = config;
40677 Roo.apply(this, config);
40681 this.resizeEl = Roo.get(this.resizeEl, true);
40683 this.resizeEl = this.el;
40685 // handle view.xtype
40693 * Fires when this panel is activated.
40694 * @param {Roo.ContentPanel} this
40698 * @event deactivate
40699 * Fires when this panel is activated.
40700 * @param {Roo.ContentPanel} this
40702 "deactivate" : true,
40706 * Fires when this panel is resized if fitToFrame is true.
40707 * @param {Roo.ContentPanel} this
40708 * @param {Number} width The width after any component adjustments
40709 * @param {Number} height The height after any component adjustments
40715 * Fires when this tab is created
40716 * @param {Roo.ContentPanel} this
40722 * Fires when this content is scrolled
40723 * @param {Roo.ContentPanel} this
40724 * @param {Event} scrollEvent
40735 if(this.autoScroll && !this.iframe){
40736 this.resizeEl.setStyle("overflow", "auto");
40737 this.resizeEl.on('scroll', this.onScroll, this);
40739 // fix randome scrolling
40740 //this.el.on('scroll', function() {
40741 // Roo.log('fix random scolling');
40742 // this.scrollTo('top',0);
40745 content = content || this.content;
40747 this.setContent(content);
40749 if(config && config.url){
40750 this.setUrl(this.url, this.params, this.loadOnce);
40755 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40757 if (this.view && typeof(this.view.xtype) != 'undefined') {
40758 this.view.el = this.el.appendChild(document.createElement("div"));
40759 this.view = Roo.factory(this.view);
40760 this.view.render && this.view.render(false, '');
40764 this.fireEvent('render', this);
40767 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40777 /* Resize Element - use this to work out scroll etc. */
40780 setRegion : function(region){
40781 this.region = region;
40782 this.setActiveClass(region && !this.background);
40786 setActiveClass: function(state)
40789 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40790 this.el.setStyle('position','relative');
40792 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40793 this.el.setStyle('position', 'absolute');
40798 * Returns the toolbar for this Panel if one was configured.
40799 * @return {Roo.Toolbar}
40801 getToolbar : function(){
40802 return this.toolbar;
40805 setActiveState : function(active)
40807 this.active = active;
40808 this.setActiveClass(active);
40810 if(this.fireEvent("deactivate", this) === false){
40815 this.fireEvent("activate", this);
40819 * Updates this panel's element (not for iframe)
40820 * @param {String} content The new content
40821 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40823 setContent : function(content, loadScripts){
40828 this.el.update(content, loadScripts);
40831 ignoreResize : function(w, h){
40832 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40835 this.lastSize = {width: w, height: h};
40840 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40841 * @return {Roo.UpdateManager} The UpdateManager
40843 getUpdateManager : function(){
40847 return this.el.getUpdateManager();
40850 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40851 * Does not work with IFRAME contents
40852 * @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:
40855 url: "your-url.php",
40856 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40857 callback: yourFunction,
40858 scope: yourObject, //(optional scope)
40861 text: "Loading...",
40867 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40868 * 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.
40869 * @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}
40870 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40871 * @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.
40872 * @return {Roo.ContentPanel} this
40880 var um = this.el.getUpdateManager();
40881 um.update.apply(um, arguments);
40887 * 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.
40888 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40889 * @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)
40890 * @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)
40891 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40893 setUrl : function(url, params, loadOnce){
40895 this.iframeEl.dom.src = url;
40899 if(this.refreshDelegate){
40900 this.removeListener("activate", this.refreshDelegate);
40902 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40903 this.on("activate", this.refreshDelegate);
40904 return this.el.getUpdateManager();
40907 _handleRefresh : function(url, params, loadOnce){
40908 if(!loadOnce || !this.loaded){
40909 var updater = this.el.getUpdateManager();
40910 updater.update(url, params, this._setLoaded.createDelegate(this));
40914 _setLoaded : function(){
40915 this.loaded = true;
40919 * Returns this panel's id
40922 getId : function(){
40927 * Returns this panel's element - used by regiosn to add.
40928 * @return {Roo.Element}
40930 getEl : function(){
40931 return this.wrapEl || this.el;
40936 adjustForComponents : function(width, height)
40938 //Roo.log('adjustForComponents ');
40939 if(this.resizeEl != this.el){
40940 width -= this.el.getFrameWidth('lr');
40941 height -= this.el.getFrameWidth('tb');
40944 var te = this.toolbar.getEl();
40945 te.setWidth(width);
40946 height -= te.getHeight();
40949 var te = this.footer.getEl();
40950 te.setWidth(width);
40951 height -= te.getHeight();
40955 if(this.adjustments){
40956 width += this.adjustments[0];
40957 height += this.adjustments[1];
40959 return {"width": width, "height": height};
40962 setSize : function(width, height){
40963 if(this.fitToFrame && !this.ignoreResize(width, height)){
40964 if(this.fitContainer && this.resizeEl != this.el){
40965 this.el.setSize(width, height);
40967 var size = this.adjustForComponents(width, height);
40969 this.iframeEl.setSize(width,height);
40972 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40973 this.fireEvent('resize', this, size.width, size.height);
40980 * Returns this panel's title
40983 getTitle : function(){
40985 if (typeof(this.title) != 'object') {
40990 for (var k in this.title) {
40991 if (!this.title.hasOwnProperty(k)) {
40995 if (k.indexOf('-') >= 0) {
40996 var s = k.split('-');
40997 for (var i = 0; i<s.length; i++) {
40998 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41001 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41008 * Set this panel's title
41009 * @param {String} title
41011 setTitle : function(title){
41012 this.title = title;
41014 this.region.updatePanelTitle(this, title);
41019 * Returns true is this panel was configured to be closable
41020 * @return {Boolean}
41022 isClosable : function(){
41023 return this.closable;
41026 beforeSlide : function(){
41028 this.resizeEl.clip();
41031 afterSlide : function(){
41033 this.resizeEl.unclip();
41037 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41038 * Will fail silently if the {@link #setUrl} method has not been called.
41039 * This does not activate the panel, just updates its content.
41041 refresh : function(){
41042 if(this.refreshDelegate){
41043 this.loaded = false;
41044 this.refreshDelegate();
41049 * Destroys this panel
41051 destroy : function(){
41052 this.el.removeAllListeners();
41053 var tempEl = document.createElement("span");
41054 tempEl.appendChild(this.el.dom);
41055 tempEl.innerHTML = "";
41061 * form - if the content panel contains a form - this is a reference to it.
41062 * @type {Roo.form.Form}
41066 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41067 * This contains a reference to it.
41073 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41083 * @param {Object} cfg Xtype definition of item to add.
41087 getChildContainer: function () {
41088 return this.getEl();
41092 onScroll : function(e)
41094 this.fireEvent('scroll', this, e);
41099 var ret = new Roo.factory(cfg);
41104 if (cfg.xtype.match(/^Form$/)) {
41107 //if (this.footer) {
41108 // el = this.footer.container.insertSibling(false, 'before');
41110 el = this.el.createChild();
41113 this.form = new Roo.form.Form(cfg);
41116 if ( this.form.allItems.length) {
41117 this.form.render(el.dom);
41121 // should only have one of theses..
41122 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41123 // views.. should not be just added - used named prop 'view''
41125 cfg.el = this.el.appendChild(document.createElement("div"));
41128 var ret = new Roo.factory(cfg);
41130 ret.render && ret.render(false, ''); // render blank..
41140 * @class Roo.bootstrap.panel.Grid
41141 * @extends Roo.bootstrap.panel.Content
41143 * Create a new GridPanel.
41144 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41145 * @param {Object} config A the config object
41151 Roo.bootstrap.panel.Grid = function(config)
41155 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41156 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41158 config.el = this.wrapper;
41159 //this.el = this.wrapper;
41161 if (config.container) {
41162 // ctor'ed from a Border/panel.grid
41165 this.wrapper.setStyle("overflow", "hidden");
41166 this.wrapper.addClass('roo-grid-container');
41171 if(config.toolbar){
41172 var tool_el = this.wrapper.createChild();
41173 this.toolbar = Roo.factory(config.toolbar);
41175 if (config.toolbar.items) {
41176 ti = config.toolbar.items ;
41177 delete config.toolbar.items ;
41181 this.toolbar.render(tool_el);
41182 for(var i =0;i < ti.length;i++) {
41183 // Roo.log(['add child', items[i]]);
41184 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41186 this.toolbar.items = nitems;
41188 delete config.toolbar;
41191 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41192 config.grid.scrollBody = true;;
41193 config.grid.monitorWindowResize = false; // turn off autosizing
41194 config.grid.autoHeight = false;
41195 config.grid.autoWidth = false;
41197 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41199 if (config.background) {
41200 // render grid on panel activation (if panel background)
41201 this.on('activate', function(gp) {
41202 if (!gp.grid.rendered) {
41203 gp.grid.render(this.wrapper);
41204 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41209 this.grid.render(this.wrapper);
41210 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41213 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41214 // ??? needed ??? config.el = this.wrapper;
41219 // xtype created footer. - not sure if will work as we normally have to render first..
41220 if (this.footer && !this.footer.el && this.footer.xtype) {
41222 var ctr = this.grid.getView().getFooterPanel(true);
41223 this.footer.dataSource = this.grid.dataSource;
41224 this.footer = Roo.factory(this.footer, Roo);
41225 this.footer.render(ctr);
41235 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41236 getId : function(){
41237 return this.grid.id;
41241 * Returns the grid for this panel
41242 * @return {Roo.bootstrap.Table}
41244 getGrid : function(){
41248 setSize : function(width, height){
41249 if(!this.ignoreResize(width, height)){
41250 var grid = this.grid;
41251 var size = this.adjustForComponents(width, height);
41252 // tfoot is not a footer?
41255 var gridel = grid.getGridEl();
41256 gridel.setSize(size.width, size.height);
41258 var tbd = grid.getGridEl().select('tbody', true).first();
41259 var thd = grid.getGridEl().select('thead',true).first();
41260 var tbf= grid.getGridEl().select('tfoot', true).first();
41263 size.height -= tbf.getHeight();
41266 size.height -= thd.getHeight();
41269 tbd.setSize(size.width, size.height );
41270 // this is for the account management tab -seems to work there.
41271 var thd = grid.getGridEl().select('thead',true).first();
41273 // tbd.setSize(size.width, size.height - thd.getHeight());
41282 beforeSlide : function(){
41283 this.grid.getView().scroller.clip();
41286 afterSlide : function(){
41287 this.grid.getView().scroller.unclip();
41290 destroy : function(){
41291 this.grid.destroy();
41293 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41298 * @class Roo.bootstrap.panel.Nest
41299 * @extends Roo.bootstrap.panel.Content
41301 * Create a new Panel, that can contain a layout.Border.
41304 * @param {Roo.BorderLayout} layout The layout for this panel
41305 * @param {String/Object} config A string to set only the title or a config object
41307 Roo.bootstrap.panel.Nest = function(config)
41309 // construct with only one argument..
41310 /* FIXME - implement nicer consturctors
41311 if (layout.layout) {
41313 layout = config.layout;
41314 delete config.layout;
41316 if (layout.xtype && !layout.getEl) {
41317 // then layout needs constructing..
41318 layout = Roo.factory(layout, Roo);
41322 config.el = config.layout.getEl();
41324 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41326 config.layout.monitorWindowResize = false; // turn off autosizing
41327 this.layout = config.layout;
41328 this.layout.getEl().addClass("roo-layout-nested-layout");
41329 this.layout.parent = this;
41336 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41338 setSize : function(width, height){
41339 if(!this.ignoreResize(width, height)){
41340 var size = this.adjustForComponents(width, height);
41341 var el = this.layout.getEl();
41342 if (size.height < 1) {
41343 el.setWidth(size.width);
41345 el.setSize(size.width, size.height);
41347 var touch = el.dom.offsetWidth;
41348 this.layout.layout();
41349 // ie requires a double layout on the first pass
41350 if(Roo.isIE && !this.initialized){
41351 this.initialized = true;
41352 this.layout.layout();
41357 // activate all subpanels if not currently active..
41359 setActiveState : function(active){
41360 this.active = active;
41361 this.setActiveClass(active);
41364 this.fireEvent("deactivate", this);
41368 this.fireEvent("activate", this);
41369 // not sure if this should happen before or after..
41370 if (!this.layout) {
41371 return; // should not happen..
41374 for (var r in this.layout.regions) {
41375 reg = this.layout.getRegion(r);
41376 if (reg.getActivePanel()) {
41377 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41378 reg.setActivePanel(reg.getActivePanel());
41381 if (!reg.panels.length) {
41384 reg.showPanel(reg.getPanel(0));
41393 * Returns the nested BorderLayout for this panel
41394 * @return {Roo.BorderLayout}
41396 getLayout : function(){
41397 return this.layout;
41401 * Adds a xtype elements to the layout of the nested panel
41405 xtype : 'ContentPanel',
41412 xtype : 'NestedLayoutPanel',
41418 items : [ ... list of content panels or nested layout panels.. ]
41422 * @param {Object} cfg Xtype definition of item to add.
41424 addxtype : function(cfg) {
41425 return this.layout.addxtype(cfg);
41430 * Ext JS Library 1.1.1
41431 * Copyright(c) 2006-2007, Ext JS, LLC.
41433 * Originally Released Under LGPL - original licence link has changed is not relivant.
41436 * <script type="text/javascript">
41439 * @class Roo.TabPanel
41440 * @extends Roo.util.Observable
41441 * A lightweight tab container.
41445 // basic tabs 1, built from existing content
41446 var tabs = new Roo.TabPanel("tabs1");
41447 tabs.addTab("script", "View Script");
41448 tabs.addTab("markup", "View Markup");
41449 tabs.activate("script");
41451 // more advanced tabs, built from javascript
41452 var jtabs = new Roo.TabPanel("jtabs");
41453 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41455 // set up the UpdateManager
41456 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41457 var updater = tab2.getUpdateManager();
41458 updater.setDefaultUrl("ajax1.htm");
41459 tab2.on('activate', updater.refresh, updater, true);
41461 // Use setUrl for Ajax loading
41462 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41463 tab3.setUrl("ajax2.htm", null, true);
41466 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41469 jtabs.activate("jtabs-1");
41472 * Create a new TabPanel.
41473 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41474 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41476 Roo.bootstrap.panel.Tabs = function(config){
41478 * The container element for this TabPanel.
41479 * @type Roo.Element
41481 this.el = Roo.get(config.el);
41484 if(typeof config == "boolean"){
41485 this.tabPosition = config ? "bottom" : "top";
41487 Roo.apply(this, config);
41491 if(this.tabPosition == "bottom"){
41492 // if tabs are at the bottom = create the body first.
41493 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41494 this.el.addClass("roo-tabs-bottom");
41496 // next create the tabs holders
41498 if (this.tabPosition == "west"){
41500 var reg = this.region; // fake it..
41502 if (!reg.mgr.parent) {
41505 reg = reg.mgr.parent.region;
41507 Roo.log("got nest?");
41509 if (reg.mgr.getRegion('west')) {
41510 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41511 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41512 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41513 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41514 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41522 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41523 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41524 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41525 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41530 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41533 // finally - if tabs are at the top, then create the body last..
41534 if(this.tabPosition != "bottom"){
41535 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41536 * @type Roo.Element
41538 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41539 this.el.addClass("roo-tabs-top");
41543 this.bodyEl.setStyle("position", "relative");
41545 this.active = null;
41546 this.activateDelegate = this.activate.createDelegate(this);
41551 * Fires when the active tab changes
41552 * @param {Roo.TabPanel} this
41553 * @param {Roo.TabPanelItem} activePanel The new active tab
41557 * @event beforetabchange
41558 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41559 * @param {Roo.TabPanel} this
41560 * @param {Object} e Set cancel to true on this object to cancel the tab change
41561 * @param {Roo.TabPanelItem} tab The tab being changed to
41563 "beforetabchange" : true
41566 Roo.EventManager.onWindowResize(this.onResize, this);
41567 this.cpad = this.el.getPadding("lr");
41568 this.hiddenCount = 0;
41571 // toolbar on the tabbar support...
41572 if (this.toolbar) {
41573 alert("no toolbar support yet");
41574 this.toolbar = false;
41576 var tcfg = this.toolbar;
41577 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41578 this.toolbar = new Roo.Toolbar(tcfg);
41579 if (Roo.isSafari) {
41580 var tbl = tcfg.container.child('table', true);
41581 tbl.setAttribute('width', '100%');
41589 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41592 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41594 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41596 tabPosition : "top",
41598 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41600 currentTabWidth : 0,
41602 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41606 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41610 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41612 preferredTabWidth : 175,
41614 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41616 resizeTabs : false,
41618 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41620 monitorResize : true,
41622 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41624 toolbar : false, // set by caller..
41626 region : false, /// set by caller
41628 disableTooltips : true, // not used yet...
41631 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41632 * @param {String} id The id of the div to use <b>or create</b>
41633 * @param {String} text The text for the tab
41634 * @param {String} content (optional) Content to put in the TabPanelItem body
41635 * @param {Boolean} closable (optional) True to create a close icon on the tab
41636 * @return {Roo.TabPanelItem} The created TabPanelItem
41638 addTab : function(id, text, content, closable, tpl)
41640 var item = new Roo.bootstrap.panel.TabItem({
41644 closable : closable,
41647 this.addTabItem(item);
41649 item.setContent(content);
41655 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41656 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41657 * @return {Roo.TabPanelItem}
41659 getTab : function(id){
41660 return this.items[id];
41664 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41665 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41667 hideTab : function(id){
41668 var t = this.items[id];
41671 this.hiddenCount++;
41672 this.autoSizeTabs();
41677 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41678 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41680 unhideTab : function(id){
41681 var t = this.items[id];
41683 t.setHidden(false);
41684 this.hiddenCount--;
41685 this.autoSizeTabs();
41690 * Adds an existing {@link Roo.TabPanelItem}.
41691 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41693 addTabItem : function(item)
41695 this.items[item.id] = item;
41696 this.items.push(item);
41697 this.autoSizeTabs();
41698 // if(this.resizeTabs){
41699 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41700 // this.autoSizeTabs();
41702 // item.autoSize();
41707 * Removes a {@link Roo.TabPanelItem}.
41708 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41710 removeTab : function(id){
41711 var items = this.items;
41712 var tab = items[id];
41713 if(!tab) { return; }
41714 var index = items.indexOf(tab);
41715 if(this.active == tab && items.length > 1){
41716 var newTab = this.getNextAvailable(index);
41721 this.stripEl.dom.removeChild(tab.pnode.dom);
41722 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41723 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41725 items.splice(index, 1);
41726 delete this.items[tab.id];
41727 tab.fireEvent("close", tab);
41728 tab.purgeListeners();
41729 this.autoSizeTabs();
41732 getNextAvailable : function(start){
41733 var items = this.items;
41735 // look for a next tab that will slide over to
41736 // replace the one being removed
41737 while(index < items.length){
41738 var item = items[++index];
41739 if(item && !item.isHidden()){
41743 // if one isn't found select the previous tab (on the left)
41746 var item = items[--index];
41747 if(item && !item.isHidden()){
41755 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41756 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41758 disableTab : function(id){
41759 var tab = this.items[id];
41760 if(tab && this.active != tab){
41766 * Enables a {@link Roo.TabPanelItem} that is disabled.
41767 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41769 enableTab : function(id){
41770 var tab = this.items[id];
41775 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41776 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41777 * @return {Roo.TabPanelItem} The TabPanelItem.
41779 activate : function(id)
41781 //Roo.log('activite:' + id);
41783 var tab = this.items[id];
41787 if(tab == this.active || tab.disabled){
41791 this.fireEvent("beforetabchange", this, e, tab);
41792 if(e.cancel !== true && !tab.disabled){
41794 this.active.hide();
41796 this.active = this.items[id];
41797 this.active.show();
41798 this.fireEvent("tabchange", this, this.active);
41804 * Gets the active {@link Roo.TabPanelItem}.
41805 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41807 getActiveTab : function(){
41808 return this.active;
41812 * Updates the tab body element to fit the height of the container element
41813 * for overflow scrolling
41814 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41816 syncHeight : function(targetHeight){
41817 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41818 var bm = this.bodyEl.getMargins();
41819 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41820 this.bodyEl.setHeight(newHeight);
41824 onResize : function(){
41825 if(this.monitorResize){
41826 this.autoSizeTabs();
41831 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41833 beginUpdate : function(){
41834 this.updating = true;
41838 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41840 endUpdate : function(){
41841 this.updating = false;
41842 this.autoSizeTabs();
41846 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41848 autoSizeTabs : function()
41850 var count = this.items.length;
41851 var vcount = count - this.hiddenCount;
41854 this.stripEl.hide();
41856 this.stripEl.show();
41859 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41864 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41865 var availWidth = Math.floor(w / vcount);
41866 var b = this.stripBody;
41867 if(b.getWidth() > w){
41868 var tabs = this.items;
41869 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41870 if(availWidth < this.minTabWidth){
41871 /*if(!this.sleft){ // incomplete scrolling code
41872 this.createScrollButtons();
41875 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41878 if(this.currentTabWidth < this.preferredTabWidth){
41879 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41885 * Returns the number of tabs in this TabPanel.
41888 getCount : function(){
41889 return this.items.length;
41893 * Resizes all the tabs to the passed width
41894 * @param {Number} The new width
41896 setTabWidth : function(width){
41897 this.currentTabWidth = width;
41898 for(var i = 0, len = this.items.length; i < len; i++) {
41899 if(!this.items[i].isHidden()) {
41900 this.items[i].setWidth(width);
41906 * Destroys this TabPanel
41907 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41909 destroy : function(removeEl){
41910 Roo.EventManager.removeResizeListener(this.onResize, this);
41911 for(var i = 0, len = this.items.length; i < len; i++){
41912 this.items[i].purgeListeners();
41914 if(removeEl === true){
41915 this.el.update("");
41920 createStrip : function(container)
41922 var strip = document.createElement("nav");
41923 strip.className = Roo.bootstrap.version == 4 ?
41924 "navbar-light bg-light" :
41925 "navbar navbar-default"; //"x-tabs-wrap";
41926 container.appendChild(strip);
41930 createStripList : function(strip)
41932 // div wrapper for retard IE
41933 // returns the "tr" element.
41934 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41935 //'<div class="x-tabs-strip-wrap">'+
41936 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41937 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41938 return strip.firstChild; //.firstChild.firstChild.firstChild;
41940 createBody : function(container)
41942 var body = document.createElement("div");
41943 Roo.id(body, "tab-body");
41944 //Roo.fly(body).addClass("x-tabs-body");
41945 Roo.fly(body).addClass("tab-content");
41946 container.appendChild(body);
41949 createItemBody :function(bodyEl, id){
41950 var body = Roo.getDom(id);
41952 body = document.createElement("div");
41955 //Roo.fly(body).addClass("x-tabs-item-body");
41956 Roo.fly(body).addClass("tab-pane");
41957 bodyEl.insertBefore(body, bodyEl.firstChild);
41961 createStripElements : function(stripEl, text, closable, tpl)
41963 var td = document.createElement("li"); // was td..
41964 td.className = 'nav-item';
41966 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41969 stripEl.appendChild(td);
41971 td.className = "x-tabs-closable";
41972 if(!this.closeTpl){
41973 this.closeTpl = new Roo.Template(
41974 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41975 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41976 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41979 var el = this.closeTpl.overwrite(td, {"text": text});
41980 var close = el.getElementsByTagName("div")[0];
41981 var inner = el.getElementsByTagName("em")[0];
41982 return {"el": el, "close": close, "inner": inner};
41985 // not sure what this is..
41986 // if(!this.tabTpl){
41987 //this.tabTpl = new Roo.Template(
41988 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41989 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41991 // this.tabTpl = new Roo.Template(
41992 // '<a href="#">' +
41993 // '<span unselectable="on"' +
41994 // (this.disableTooltips ? '' : ' title="{text}"') +
41995 // ' >{text}</span></a>'
42001 var template = tpl || this.tabTpl || false;
42004 template = new Roo.Template(
42005 Roo.bootstrap.version == 4 ?
42007 '<a class="nav-link" href="#" unselectable="on"' +
42008 (this.disableTooltips ? '' : ' title="{text}"') +
42011 '<a class="nav-link" href="#">' +
42012 '<span unselectable="on"' +
42013 (this.disableTooltips ? '' : ' title="{text}"') +
42014 ' >{text}</span></a>'
42019 switch (typeof(template)) {
42023 template = new Roo.Template(template);
42029 var el = template.overwrite(td, {"text": text});
42031 var inner = el.getElementsByTagName("span")[0];
42033 return {"el": el, "inner": inner};
42041 * @class Roo.TabPanelItem
42042 * @extends Roo.util.Observable
42043 * Represents an individual item (tab plus body) in a TabPanel.
42044 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42045 * @param {String} id The id of this TabPanelItem
42046 * @param {String} text The text for the tab of this TabPanelItem
42047 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42049 Roo.bootstrap.panel.TabItem = function(config){
42051 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42052 * @type Roo.TabPanel
42054 this.tabPanel = config.panel;
42056 * The id for this TabPanelItem
42059 this.id = config.id;
42061 this.disabled = false;
42063 this.text = config.text;
42065 this.loaded = false;
42066 this.closable = config.closable;
42069 * The body element for this TabPanelItem.
42070 * @type Roo.Element
42072 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42073 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42074 this.bodyEl.setStyle("display", "block");
42075 this.bodyEl.setStyle("zoom", "1");
42076 //this.hideAction();
42078 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42080 this.el = Roo.get(els.el);
42081 this.inner = Roo.get(els.inner, true);
42082 this.textEl = Roo.bootstrap.version == 4 ?
42083 this.el : Roo.get(this.el.dom.firstChild, true);
42085 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42086 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42089 // this.el.on("mousedown", this.onTabMouseDown, this);
42090 this.el.on("click", this.onTabClick, this);
42092 if(config.closable){
42093 var c = Roo.get(els.close, true);
42094 c.dom.title = this.closeText;
42095 c.addClassOnOver("close-over");
42096 c.on("click", this.closeClick, this);
42102 * Fires when this tab becomes the active tab.
42103 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42104 * @param {Roo.TabPanelItem} this
42108 * @event beforeclose
42109 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42110 * @param {Roo.TabPanelItem} this
42111 * @param {Object} e Set cancel to true on this object to cancel the close.
42113 "beforeclose": true,
42116 * Fires when this tab is closed.
42117 * @param {Roo.TabPanelItem} this
42121 * @event deactivate
42122 * Fires when this tab is no longer the active tab.
42123 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42124 * @param {Roo.TabPanelItem} this
42126 "deactivate" : true
42128 this.hidden = false;
42130 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42133 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42135 purgeListeners : function(){
42136 Roo.util.Observable.prototype.purgeListeners.call(this);
42137 this.el.removeAllListeners();
42140 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42143 this.status_node.addClass("active");
42146 this.tabPanel.stripWrap.repaint();
42148 this.fireEvent("activate", this.tabPanel, this);
42152 * Returns true if this tab is the active tab.
42153 * @return {Boolean}
42155 isActive : function(){
42156 return this.tabPanel.getActiveTab() == this;
42160 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42163 this.status_node.removeClass("active");
42165 this.fireEvent("deactivate", this.tabPanel, this);
42168 hideAction : function(){
42169 this.bodyEl.hide();
42170 this.bodyEl.setStyle("position", "absolute");
42171 this.bodyEl.setLeft("-20000px");
42172 this.bodyEl.setTop("-20000px");
42175 showAction : function(){
42176 this.bodyEl.setStyle("position", "relative");
42177 this.bodyEl.setTop("");
42178 this.bodyEl.setLeft("");
42179 this.bodyEl.show();
42183 * Set the tooltip for the tab.
42184 * @param {String} tooltip The tab's tooltip
42186 setTooltip : function(text){
42187 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42188 this.textEl.dom.qtip = text;
42189 this.textEl.dom.removeAttribute('title');
42191 this.textEl.dom.title = text;
42195 onTabClick : function(e){
42196 e.preventDefault();
42197 this.tabPanel.activate(this.id);
42200 onTabMouseDown : function(e){
42201 e.preventDefault();
42202 this.tabPanel.activate(this.id);
42205 getWidth : function(){
42206 return this.inner.getWidth();
42209 setWidth : function(width){
42210 var iwidth = width - this.linode.getPadding("lr");
42211 this.inner.setWidth(iwidth);
42212 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42213 this.linode.setWidth(width);
42217 * Show or hide the tab
42218 * @param {Boolean} hidden True to hide or false to show.
42220 setHidden : function(hidden){
42221 this.hidden = hidden;
42222 this.linode.setStyle("display", hidden ? "none" : "");
42226 * Returns true if this tab is "hidden"
42227 * @return {Boolean}
42229 isHidden : function(){
42230 return this.hidden;
42234 * Returns the text for this tab
42237 getText : function(){
42241 autoSize : function(){
42242 //this.el.beginMeasure();
42243 this.textEl.setWidth(1);
42245 * #2804 [new] Tabs in Roojs
42246 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42248 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42249 //this.el.endMeasure();
42253 * Sets the text for the tab (Note: this also sets the tooltip text)
42254 * @param {String} text The tab's text and tooltip
42256 setText : function(text){
42258 this.textEl.update(text);
42259 this.setTooltip(text);
42260 //if(!this.tabPanel.resizeTabs){
42261 // this.autoSize();
42265 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42267 activate : function(){
42268 this.tabPanel.activate(this.id);
42272 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42274 disable : function(){
42275 if(this.tabPanel.active != this){
42276 this.disabled = true;
42277 this.status_node.addClass("disabled");
42282 * Enables this TabPanelItem if it was previously disabled.
42284 enable : function(){
42285 this.disabled = false;
42286 this.status_node.removeClass("disabled");
42290 * Sets the content for this TabPanelItem.
42291 * @param {String} content The content
42292 * @param {Boolean} loadScripts true to look for and load scripts
42294 setContent : function(content, loadScripts){
42295 this.bodyEl.update(content, loadScripts);
42299 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42300 * @return {Roo.UpdateManager} The UpdateManager
42302 getUpdateManager : function(){
42303 return this.bodyEl.getUpdateManager();
42307 * Set a URL to be used to load the content for this TabPanelItem.
42308 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42309 * @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)
42310 * @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)
42311 * @return {Roo.UpdateManager} The UpdateManager
42313 setUrl : function(url, params, loadOnce){
42314 if(this.refreshDelegate){
42315 this.un('activate', this.refreshDelegate);
42317 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42318 this.on("activate", this.refreshDelegate);
42319 return this.bodyEl.getUpdateManager();
42323 _handleRefresh : function(url, params, loadOnce){
42324 if(!loadOnce || !this.loaded){
42325 var updater = this.bodyEl.getUpdateManager();
42326 updater.update(url, params, this._setLoaded.createDelegate(this));
42331 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42332 * Will fail silently if the setUrl method has not been called.
42333 * This does not activate the panel, just updates its content.
42335 refresh : function(){
42336 if(this.refreshDelegate){
42337 this.loaded = false;
42338 this.refreshDelegate();
42343 _setLoaded : function(){
42344 this.loaded = true;
42348 closeClick : function(e){
42351 this.fireEvent("beforeclose", this, o);
42352 if(o.cancel !== true){
42353 this.tabPanel.removeTab(this.id);
42357 * The text displayed in the tooltip for the close icon.
42360 closeText : "Close this tab"
42363 * This script refer to:
42364 * Title: International Telephone Input
42365 * Author: Jack O'Connor
42366 * Code version: v12.1.12
42367 * Availability: https://github.com/jackocnr/intl-tel-input.git
42370 Roo.bootstrap.PhoneInputData = function() {
42373 "Afghanistan (افغانستان)",
42378 "Albania (Shqipëri)",
42383 "Algeria (الجزائر)",
42408 "Antigua and Barbuda",
42418 "Armenia (Հայաստան)",
42434 "Austria (Österreich)",
42439 "Azerbaijan (Azərbaycan)",
42449 "Bahrain (البحرين)",
42454 "Bangladesh (বাংলাদেশ)",
42464 "Belarus (Беларусь)",
42469 "Belgium (België)",
42499 "Bosnia and Herzegovina (Босна и Херцеговина)",
42514 "British Indian Ocean Territory",
42519 "British Virgin Islands",
42529 "Bulgaria (България)",
42539 "Burundi (Uburundi)",
42544 "Cambodia (កម្ពុជា)",
42549 "Cameroon (Cameroun)",
42558 ["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"]
42561 "Cape Verde (Kabu Verdi)",
42566 "Caribbean Netherlands",
42577 "Central African Republic (République centrafricaine)",
42597 "Christmas Island",
42603 "Cocos (Keeling) Islands",
42614 "Comoros (جزر القمر)",
42619 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42624 "Congo (Republic) (Congo-Brazzaville)",
42644 "Croatia (Hrvatska)",
42665 "Czech Republic (Česká republika)",
42670 "Denmark (Danmark)",
42685 "Dominican Republic (República Dominicana)",
42689 ["809", "829", "849"]
42707 "Equatorial Guinea (Guinea Ecuatorial)",
42727 "Falkland Islands (Islas Malvinas)",
42732 "Faroe Islands (Føroyar)",
42753 "French Guiana (Guyane française)",
42758 "French Polynesia (Polynésie française)",
42773 "Georgia (საქართველო)",
42778 "Germany (Deutschland)",
42798 "Greenland (Kalaallit Nunaat)",
42835 "Guinea-Bissau (Guiné Bissau)",
42860 "Hungary (Magyarország)",
42865 "Iceland (Ísland)",
42885 "Iraq (العراق)",
42901 "Israel (ישראל)",
42928 "Jordan (الأردن)",
42933 "Kazakhstan (Казахстан)",
42954 "Kuwait (الكويت)",
42959 "Kyrgyzstan (Кыргызстан)",
42969 "Latvia (Latvija)",
42974 "Lebanon (لبنان)",
42989 "Libya (ليبيا)",
42999 "Lithuania (Lietuva)",
43014 "Macedonia (FYROM) (Македонија)",
43019 "Madagascar (Madagasikara)",
43049 "Marshall Islands",
43059 "Mauritania (موريتانيا)",
43064 "Mauritius (Moris)",
43085 "Moldova (Republica Moldova)",
43095 "Mongolia (Монгол)",
43100 "Montenegro (Crna Gora)",
43110 "Morocco (المغرب)",
43116 "Mozambique (Moçambique)",
43121 "Myanmar (Burma) (မြန်မာ)",
43126 "Namibia (Namibië)",
43141 "Netherlands (Nederland)",
43146 "New Caledonia (Nouvelle-Calédonie)",
43181 "North Korea (조선 민주주의 인민 공화국)",
43186 "Northern Mariana Islands",
43202 "Pakistan (پاکستان)",
43212 "Palestine (فلسطين)",
43222 "Papua New Guinea",
43264 "Réunion (La Réunion)",
43270 "Romania (România)",
43286 "Saint Barthélemy",
43297 "Saint Kitts and Nevis",
43307 "Saint Martin (Saint-Martin (partie française))",
43313 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43318 "Saint Vincent and the Grenadines",
43333 "São Tomé and Príncipe (São Tomé e Príncipe)",
43338 "Saudi Arabia (المملكة العربية السعودية)",
43343 "Senegal (Sénégal)",
43373 "Slovakia (Slovensko)",
43378 "Slovenia (Slovenija)",
43388 "Somalia (Soomaaliya)",
43398 "South Korea (대한민국)",
43403 "South Sudan (جنوب السودان)",
43413 "Sri Lanka (ශ්රී ලංකාව)",
43418 "Sudan (السودان)",
43428 "Svalbard and Jan Mayen",
43439 "Sweden (Sverige)",
43444 "Switzerland (Schweiz)",
43449 "Syria (سوريا)",
43494 "Trinidad and Tobago",
43499 "Tunisia (تونس)",
43504 "Turkey (Türkiye)",
43514 "Turks and Caicos Islands",
43524 "U.S. Virgin Islands",
43534 "Ukraine (Україна)",
43539 "United Arab Emirates (الإمارات العربية المتحدة)",
43561 "Uzbekistan (Oʻzbekiston)",
43571 "Vatican City (Città del Vaticano)",
43582 "Vietnam (Việt Nam)",
43587 "Wallis and Futuna (Wallis-et-Futuna)",
43592 "Western Sahara (الصحراء الغربية)",
43598 "Yemen (اليمن)",
43622 * This script refer to:
43623 * Title: International Telephone Input
43624 * Author: Jack O'Connor
43625 * Code version: v12.1.12
43626 * Availability: https://github.com/jackocnr/intl-tel-input.git
43630 * @class Roo.bootstrap.PhoneInput
43631 * @extends Roo.bootstrap.TriggerField
43632 * An input with International dial-code selection
43634 * @cfg {String} defaultDialCode default '+852'
43635 * @cfg {Array} preferedCountries default []
43638 * Create a new PhoneInput.
43639 * @param {Object} config Configuration options
43642 Roo.bootstrap.PhoneInput = function(config) {
43643 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43646 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43648 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43650 listWidth: undefined,
43652 selectedClass: 'active',
43654 invalidClass : "has-warning",
43656 validClass: 'has-success',
43658 allowed: '0123456789',
43663 * @cfg {String} defaultDialCode The default dial code when initializing the input
43665 defaultDialCode: '+852',
43668 * @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
43670 preferedCountries: false,
43672 getAutoCreate : function()
43674 var data = Roo.bootstrap.PhoneInputData();
43675 var align = this.labelAlign || this.parentLabelAlign();
43678 this.allCountries = [];
43679 this.dialCodeMapping = [];
43681 for (var i = 0; i < data.length; i++) {
43683 this.allCountries[i] = {
43687 priority: c[3] || 0,
43688 areaCodes: c[4] || null
43690 this.dialCodeMapping[c[2]] = {
43693 priority: c[3] || 0,
43694 areaCodes: c[4] || null
43706 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43707 maxlength: this.max_length,
43708 cls : 'form-control tel-input',
43709 autocomplete: 'new-password'
43712 var hiddenInput = {
43715 cls: 'hidden-tel-input'
43719 hiddenInput.name = this.name;
43722 if (this.disabled) {
43723 input.disabled = true;
43726 var flag_container = {
43743 cls: this.hasFeedback ? 'has-feedback' : '',
43749 cls: 'dial-code-holder',
43756 cls: 'roo-select2-container input-group',
43763 if (this.fieldLabel.length) {
43766 tooltip: 'This field is required'
43772 cls: 'control-label',
43778 html: this.fieldLabel
43781 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43787 if(this.indicatorpos == 'right') {
43788 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43795 if(align == 'left') {
43803 if(this.labelWidth > 12){
43804 label.style = "width: " + this.labelWidth + 'px';
43806 if(this.labelWidth < 13 && this.labelmd == 0){
43807 this.labelmd = this.labelWidth;
43809 if(this.labellg > 0){
43810 label.cls += ' col-lg-' + this.labellg;
43811 input.cls += ' col-lg-' + (12 - this.labellg);
43813 if(this.labelmd > 0){
43814 label.cls += ' col-md-' + this.labelmd;
43815 container.cls += ' col-md-' + (12 - this.labelmd);
43817 if(this.labelsm > 0){
43818 label.cls += ' col-sm-' + this.labelsm;
43819 container.cls += ' col-sm-' + (12 - this.labelsm);
43821 if(this.labelxs > 0){
43822 label.cls += ' col-xs-' + this.labelxs;
43823 container.cls += ' col-xs-' + (12 - this.labelxs);
43833 var settings = this;
43835 ['xs','sm','md','lg'].map(function(size){
43836 if (settings[size]) {
43837 cfg.cls += ' col-' + size + '-' + settings[size];
43841 this.store = new Roo.data.Store({
43842 proxy : new Roo.data.MemoryProxy({}),
43843 reader : new Roo.data.JsonReader({
43854 'name' : 'dialCode',
43858 'name' : 'priority',
43862 'name' : 'areaCodes',
43869 if(!this.preferedCountries) {
43870 this.preferedCountries = [
43877 var p = this.preferedCountries.reverse();
43880 for (var i = 0; i < p.length; i++) {
43881 for (var j = 0; j < this.allCountries.length; j++) {
43882 if(this.allCountries[j].iso2 == p[i]) {
43883 var t = this.allCountries[j];
43884 this.allCountries.splice(j,1);
43885 this.allCountries.unshift(t);
43891 this.store.proxy.data = {
43893 data: this.allCountries
43899 initEvents : function()
43902 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43904 this.indicator = this.indicatorEl();
43905 this.flag = this.flagEl();
43906 this.dialCodeHolder = this.dialCodeHolderEl();
43908 this.trigger = this.el.select('div.flag-box',true).first();
43909 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43914 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43915 _this.list.setWidth(lw);
43918 this.list.on('mouseover', this.onViewOver, this);
43919 this.list.on('mousemove', this.onViewMove, this);
43920 this.inputEl().on("keyup", this.onKeyUp, this);
43921 this.inputEl().on("keypress", this.onKeyPress, this);
43923 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43925 this.view = new Roo.View(this.list, this.tpl, {
43926 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43929 this.view.on('click', this.onViewClick, this);
43930 this.setValue(this.defaultDialCode);
43933 onTriggerClick : function(e)
43935 Roo.log('trigger click');
43940 if(this.isExpanded()){
43942 this.hasFocus = false;
43944 this.store.load({});
43945 this.hasFocus = true;
43950 isExpanded : function()
43952 return this.list.isVisible();
43955 collapse : function()
43957 if(!this.isExpanded()){
43961 Roo.get(document).un('mousedown', this.collapseIf, this);
43962 Roo.get(document).un('mousewheel', this.collapseIf, this);
43963 this.fireEvent('collapse', this);
43967 expand : function()
43971 if(this.isExpanded() || !this.hasFocus){
43975 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43976 this.list.setWidth(lw);
43979 this.restrictHeight();
43981 Roo.get(document).on('mousedown', this.collapseIf, this);
43982 Roo.get(document).on('mousewheel', this.collapseIf, this);
43984 this.fireEvent('expand', this);
43987 restrictHeight : function()
43989 this.list.alignTo(this.inputEl(), this.listAlign);
43990 this.list.alignTo(this.inputEl(), this.listAlign);
43993 onViewOver : function(e, t)
43995 if(this.inKeyMode){
43998 var item = this.view.findItemFromChild(t);
44001 var index = this.view.indexOf(item);
44002 this.select(index, false);
44007 onViewClick : function(view, doFocus, el, e)
44009 var index = this.view.getSelectedIndexes()[0];
44011 var r = this.store.getAt(index);
44014 this.onSelect(r, index);
44016 if(doFocus !== false && !this.blockFocus){
44017 this.inputEl().focus();
44021 onViewMove : function(e, t)
44023 this.inKeyMode = false;
44026 select : function(index, scrollIntoView)
44028 this.selectedIndex = index;
44029 this.view.select(index);
44030 if(scrollIntoView !== false){
44031 var el = this.view.getNode(index);
44033 this.list.scrollChildIntoView(el, false);
44038 createList : function()
44040 this.list = Roo.get(document.body).createChild({
44042 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44043 style: 'display:none'
44046 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44049 collapseIf : function(e)
44051 var in_combo = e.within(this.el);
44052 var in_list = e.within(this.list);
44053 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44055 if (in_combo || in_list || is_list) {
44061 onSelect : function(record, index)
44063 if(this.fireEvent('beforeselect', this, record, index) !== false){
44065 this.setFlagClass(record.data.iso2);
44066 this.setDialCode(record.data.dialCode);
44067 this.hasFocus = false;
44069 this.fireEvent('select', this, record, index);
44073 flagEl : function()
44075 var flag = this.el.select('div.flag',true).first();
44082 dialCodeHolderEl : function()
44084 var d = this.el.select('input.dial-code-holder',true).first();
44091 setDialCode : function(v)
44093 this.dialCodeHolder.dom.value = '+'+v;
44096 setFlagClass : function(n)
44098 this.flag.dom.className = 'flag '+n;
44101 getValue : function()
44103 var v = this.inputEl().getValue();
44104 if(this.dialCodeHolder) {
44105 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44110 setValue : function(v)
44112 var d = this.getDialCode(v);
44114 //invalid dial code
44115 if(v.length == 0 || !d || d.length == 0) {
44117 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44118 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44124 this.setFlagClass(this.dialCodeMapping[d].iso2);
44125 this.setDialCode(d);
44126 this.inputEl().dom.value = v.replace('+'+d,'');
44127 this.hiddenEl().dom.value = this.getValue();
44132 getDialCode : function(v)
44136 if (v.length == 0) {
44137 return this.dialCodeHolder.dom.value;
44141 if (v.charAt(0) != "+") {
44144 var numericChars = "";
44145 for (var i = 1; i < v.length; i++) {
44146 var c = v.charAt(i);
44149 if (this.dialCodeMapping[numericChars]) {
44150 dialCode = v.substr(1, i);
44152 if (numericChars.length == 4) {
44162 this.setValue(this.defaultDialCode);
44166 hiddenEl : function()
44168 return this.el.select('input.hidden-tel-input',true).first();
44171 // after setting val
44172 onKeyUp : function(e){
44173 this.setValue(this.getValue());
44176 onKeyPress : function(e){
44177 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44184 * @class Roo.bootstrap.MoneyField
44185 * @extends Roo.bootstrap.ComboBox
44186 * Bootstrap MoneyField class
44189 * Create a new MoneyField.
44190 * @param {Object} config Configuration options
44193 Roo.bootstrap.MoneyField = function(config) {
44195 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44199 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44202 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44204 allowDecimals : true,
44206 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44208 decimalSeparator : ".",
44210 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44212 decimalPrecision : 0,
44214 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44216 allowNegative : true,
44218 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44222 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44224 minValue : Number.NEGATIVE_INFINITY,
44226 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44228 maxValue : Number.MAX_VALUE,
44230 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44232 minText : "The minimum value for this field is {0}",
44234 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44236 maxText : "The maximum value for this field is {0}",
44238 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44239 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44241 nanText : "{0} is not a valid number",
44243 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44247 * @cfg {String} defaults currency of the MoneyField
44248 * value should be in lkey
44250 defaultCurrency : false,
44252 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44254 thousandsDelimiter : false,
44256 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44267 getAutoCreate : function()
44269 var align = this.labelAlign || this.parentLabelAlign();
44281 cls : 'form-control roo-money-amount-input',
44282 autocomplete: 'new-password'
44285 var hiddenInput = {
44289 cls: 'hidden-number-input'
44292 if(this.max_length) {
44293 input.maxlength = this.max_length;
44297 hiddenInput.name = this.name;
44300 if (this.disabled) {
44301 input.disabled = true;
44304 var clg = 12 - this.inputlg;
44305 var cmd = 12 - this.inputmd;
44306 var csm = 12 - this.inputsm;
44307 var cxs = 12 - this.inputxs;
44311 cls : 'row roo-money-field',
44315 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44319 cls: 'roo-select2-container input-group',
44323 cls : 'form-control roo-money-currency-input',
44324 autocomplete: 'new-password',
44326 name : this.currencyName
44330 cls : 'input-group-addon',
44344 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44348 cls: this.hasFeedback ? 'has-feedback' : '',
44359 if (this.fieldLabel.length) {
44362 tooltip: 'This field is required'
44368 cls: 'control-label',
44374 html: this.fieldLabel
44377 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44383 if(this.indicatorpos == 'right') {
44384 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44391 if(align == 'left') {
44399 if(this.labelWidth > 12){
44400 label.style = "width: " + this.labelWidth + 'px';
44402 if(this.labelWidth < 13 && this.labelmd == 0){
44403 this.labelmd = this.labelWidth;
44405 if(this.labellg > 0){
44406 label.cls += ' col-lg-' + this.labellg;
44407 input.cls += ' col-lg-' + (12 - this.labellg);
44409 if(this.labelmd > 0){
44410 label.cls += ' col-md-' + this.labelmd;
44411 container.cls += ' col-md-' + (12 - this.labelmd);
44413 if(this.labelsm > 0){
44414 label.cls += ' col-sm-' + this.labelsm;
44415 container.cls += ' col-sm-' + (12 - this.labelsm);
44417 if(this.labelxs > 0){
44418 label.cls += ' col-xs-' + this.labelxs;
44419 container.cls += ' col-xs-' + (12 - this.labelxs);
44430 var settings = this;
44432 ['xs','sm','md','lg'].map(function(size){
44433 if (settings[size]) {
44434 cfg.cls += ' col-' + size + '-' + settings[size];
44441 initEvents : function()
44443 this.indicator = this.indicatorEl();
44445 this.initCurrencyEvent();
44447 this.initNumberEvent();
44450 initCurrencyEvent : function()
44453 throw "can not find store for combo";
44456 this.store = Roo.factory(this.store, Roo.data);
44457 this.store.parent = this;
44461 this.triggerEl = this.el.select('.input-group-addon', true).first();
44463 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44468 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44469 _this.list.setWidth(lw);
44472 this.list.on('mouseover', this.onViewOver, this);
44473 this.list.on('mousemove', this.onViewMove, this);
44474 this.list.on('scroll', this.onViewScroll, this);
44477 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44480 this.view = new Roo.View(this.list, this.tpl, {
44481 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44484 this.view.on('click', this.onViewClick, this);
44486 this.store.on('beforeload', this.onBeforeLoad, this);
44487 this.store.on('load', this.onLoad, this);
44488 this.store.on('loadexception', this.onLoadException, this);
44490 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44491 "up" : function(e){
44492 this.inKeyMode = true;
44496 "down" : function(e){
44497 if(!this.isExpanded()){
44498 this.onTriggerClick();
44500 this.inKeyMode = true;
44505 "enter" : function(e){
44508 if(this.fireEvent("specialkey", this, e)){
44509 this.onViewClick(false);
44515 "esc" : function(e){
44519 "tab" : function(e){
44522 if(this.fireEvent("specialkey", this, e)){
44523 this.onViewClick(false);
44531 doRelay : function(foo, bar, hname){
44532 if(hname == 'down' || this.scope.isExpanded()){
44533 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44541 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44545 initNumberEvent : function(e)
44547 this.inputEl().on("keydown" , this.fireKey, this);
44548 this.inputEl().on("focus", this.onFocus, this);
44549 this.inputEl().on("blur", this.onBlur, this);
44551 this.inputEl().relayEvent('keyup', this);
44553 if(this.indicator){
44554 this.indicator.addClass('invisible');
44557 this.originalValue = this.getValue();
44559 if(this.validationEvent == 'keyup'){
44560 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44561 this.inputEl().on('keyup', this.filterValidation, this);
44563 else if(this.validationEvent !== false){
44564 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44567 if(this.selectOnFocus){
44568 this.on("focus", this.preFocus, this);
44571 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44572 this.inputEl().on("keypress", this.filterKeys, this);
44574 this.inputEl().relayEvent('keypress', this);
44577 var allowed = "0123456789";
44579 if(this.allowDecimals){
44580 allowed += this.decimalSeparator;
44583 if(this.allowNegative){
44587 if(this.thousandsDelimiter) {
44591 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44593 var keyPress = function(e){
44595 var k = e.getKey();
44597 var c = e.getCharCode();
44600 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44601 allowed.indexOf(String.fromCharCode(c)) === -1
44607 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44611 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44616 this.inputEl().on("keypress", keyPress, this);
44620 onTriggerClick : function(e)
44627 this.loadNext = false;
44629 if(this.isExpanded()){
44634 this.hasFocus = true;
44636 if(this.triggerAction == 'all') {
44637 this.doQuery(this.allQuery, true);
44641 this.doQuery(this.getRawValue());
44644 getCurrency : function()
44646 var v = this.currencyEl().getValue();
44651 restrictHeight : function()
44653 this.list.alignTo(this.currencyEl(), this.listAlign);
44654 this.list.alignTo(this.currencyEl(), this.listAlign);
44657 onViewClick : function(view, doFocus, el, e)
44659 var index = this.view.getSelectedIndexes()[0];
44661 var r = this.store.getAt(index);
44664 this.onSelect(r, index);
44668 onSelect : function(record, index){
44670 if(this.fireEvent('beforeselect', this, record, index) !== false){
44672 this.setFromCurrencyData(index > -1 ? record.data : false);
44676 this.fireEvent('select', this, record, index);
44680 setFromCurrencyData : function(o)
44684 this.lastCurrency = o;
44686 if (this.currencyField) {
44687 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44689 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44692 this.lastSelectionText = currency;
44694 //setting default currency
44695 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44696 this.setCurrency(this.defaultCurrency);
44700 this.setCurrency(currency);
44703 setFromData : function(o)
44707 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44709 this.setFromCurrencyData(c);
44714 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44716 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44719 this.setValue(value);
44723 setCurrency : function(v)
44725 this.currencyValue = v;
44728 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44733 setValue : function(v)
44735 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44741 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44743 this.inputEl().dom.value = (v == '') ? '' :
44744 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44746 if(!this.allowZero && v === '0') {
44747 this.hiddenEl().dom.value = '';
44748 this.inputEl().dom.value = '';
44755 getRawValue : function()
44757 var v = this.inputEl().getValue();
44762 getValue : function()
44764 return this.fixPrecision(this.parseValue(this.getRawValue()));
44767 parseValue : function(value)
44769 if(this.thousandsDelimiter) {
44771 r = new RegExp(",", "g");
44772 value = value.replace(r, "");
44775 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44776 return isNaN(value) ? '' : value;
44780 fixPrecision : function(value)
44782 if(this.thousandsDelimiter) {
44784 r = new RegExp(",", "g");
44785 value = value.replace(r, "");
44788 var nan = isNaN(value);
44790 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44791 return nan ? '' : value;
44793 return parseFloat(value).toFixed(this.decimalPrecision);
44796 decimalPrecisionFcn : function(v)
44798 return Math.floor(v);
44801 validateValue : function(value)
44803 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44807 var num = this.parseValue(value);
44810 this.markInvalid(String.format(this.nanText, value));
44814 if(num < this.minValue){
44815 this.markInvalid(String.format(this.minText, this.minValue));
44819 if(num > this.maxValue){
44820 this.markInvalid(String.format(this.maxText, this.maxValue));
44827 validate : function()
44829 if(this.disabled || this.allowBlank){
44834 var currency = this.getCurrency();
44836 if(this.validateValue(this.getRawValue()) && currency.length){
44841 this.markInvalid();
44845 getName: function()
44850 beforeBlur : function()
44856 var v = this.parseValue(this.getRawValue());
44863 onBlur : function()
44867 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44868 //this.el.removeClass(this.focusClass);
44871 this.hasFocus = false;
44873 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44877 var v = this.getValue();
44879 if(String(v) !== String(this.startValue)){
44880 this.fireEvent('change', this, v, this.startValue);
44883 this.fireEvent("blur", this);
44886 inputEl : function()
44888 return this.el.select('.roo-money-amount-input', true).first();
44891 currencyEl : function()
44893 return this.el.select('.roo-money-currency-input', true).first();
44896 hiddenEl : function()
44898 return this.el.select('input.hidden-number-input',true).first();
44902 * @class Roo.bootstrap.BezierSignature
44903 * @extends Roo.bootstrap.Component
44904 * Bootstrap BezierSignature class
44905 * This script refer to:
44906 * Title: Signature Pad
44908 * Availability: https://github.com/szimek/signature_pad
44911 * Create a new BezierSignature
44912 * @param {Object} config The config object
44915 Roo.bootstrap.BezierSignature = function(config){
44916 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44922 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44929 mouse_btn_down: true,
44932 * @cfg {int} canvas height
44934 canvas_height: '200px',
44937 * @cfg {float|function} Radius of a single dot.
44942 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44947 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44952 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44957 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44962 * @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.
44964 bg_color: 'rgba(0, 0, 0, 0)',
44967 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44969 dot_color: 'black',
44972 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44974 velocity_filter_weight: 0.7,
44977 * @cfg {function} Callback when stroke begin.
44982 * @cfg {function} Callback when stroke end.
44986 getAutoCreate : function()
44988 var cls = 'roo-signature column';
44991 cls += ' ' + this.cls;
45001 for(var i = 0; i < col_sizes.length; i++) {
45002 if(this[col_sizes[i]]) {
45003 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45013 cls: 'roo-signature-body',
45017 cls: 'roo-signature-body-canvas',
45018 height: this.canvas_height,
45019 width: this.canvas_width
45026 style: 'display: none'
45034 initEvents: function()
45036 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45038 var canvas = this.canvasEl();
45040 // mouse && touch event swapping...
45041 canvas.dom.style.touchAction = 'none';
45042 canvas.dom.style.msTouchAction = 'none';
45044 this.mouse_btn_down = false;
45045 canvas.on('mousedown', this._handleMouseDown, this);
45046 canvas.on('mousemove', this._handleMouseMove, this);
45047 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45049 if (window.PointerEvent) {
45050 canvas.on('pointerdown', this._handleMouseDown, this);
45051 canvas.on('pointermove', this._handleMouseMove, this);
45052 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45055 if ('ontouchstart' in window) {
45056 canvas.on('touchstart', this._handleTouchStart, this);
45057 canvas.on('touchmove', this._handleTouchMove, this);
45058 canvas.on('touchend', this._handleTouchEnd, this);
45061 Roo.EventManager.onWindowResize(this.resize, this, true);
45063 // file input event
45064 this.fileEl().on('change', this.uploadImage, this);
45071 resize: function(){
45073 var canvas = this.canvasEl().dom;
45074 var ctx = this.canvasElCtx();
45075 var img_data = false;
45077 if(canvas.width > 0) {
45078 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45080 // setting canvas width will clean img data
45083 var style = window.getComputedStyle ?
45084 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45086 var padding_left = parseInt(style.paddingLeft) || 0;
45087 var padding_right = parseInt(style.paddingRight) || 0;
45089 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45092 ctx.putImageData(img_data, 0, 0);
45096 _handleMouseDown: function(e)
45098 if (e.browserEvent.which === 1) {
45099 this.mouse_btn_down = true;
45100 this.strokeBegin(e);
45104 _handleMouseMove: function (e)
45106 if (this.mouse_btn_down) {
45107 this.strokeMoveUpdate(e);
45111 _handleMouseUp: function (e)
45113 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45114 this.mouse_btn_down = false;
45119 _handleTouchStart: function (e) {
45121 e.preventDefault();
45122 if (e.browserEvent.targetTouches.length === 1) {
45123 // var touch = e.browserEvent.changedTouches[0];
45124 // this.strokeBegin(touch);
45126 this.strokeBegin(e); // assume e catching the correct xy...
45130 _handleTouchMove: function (e) {
45131 e.preventDefault();
45132 // var touch = event.targetTouches[0];
45133 // _this._strokeMoveUpdate(touch);
45134 this.strokeMoveUpdate(e);
45137 _handleTouchEnd: function (e) {
45138 var wasCanvasTouched = e.target === this.canvasEl().dom;
45139 if (wasCanvasTouched) {
45140 e.preventDefault();
45141 // var touch = event.changedTouches[0];
45142 // _this._strokeEnd(touch);
45147 reset: function () {
45148 this._lastPoints = [];
45149 this._lastVelocity = 0;
45150 this._lastWidth = (this.min_width + this.max_width) / 2;
45151 this.canvasElCtx().fillStyle = this.dot_color;
45154 strokeMoveUpdate: function(e)
45156 this.strokeUpdate(e);
45158 if (this.throttle) {
45159 this.throttleStroke(this.strokeUpdate, this.throttle);
45162 this.strokeUpdate(e);
45166 strokeBegin: function(e)
45168 var newPointGroup = {
45169 color: this.dot_color,
45173 if (typeof this.onBegin === 'function') {
45177 this.curve_data.push(newPointGroup);
45179 this.strokeUpdate(e);
45182 strokeUpdate: function(e)
45184 var rect = this.canvasEl().dom.getBoundingClientRect();
45185 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45186 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45187 var lastPoints = lastPointGroup.points;
45188 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45189 var isLastPointTooClose = lastPoint
45190 ? point.distanceTo(lastPoint) <= this.min_distance
45192 var color = lastPointGroup.color;
45193 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45194 var curve = this.addPoint(point);
45196 this.drawDot({color: color, point: point});
45199 this.drawCurve({color: color, curve: curve});
45209 strokeEnd: function(e)
45211 this.strokeUpdate(e);
45212 if (typeof this.onEnd === 'function') {
45217 addPoint: function (point) {
45218 var _lastPoints = this._lastPoints;
45219 _lastPoints.push(point);
45220 if (_lastPoints.length > 2) {
45221 if (_lastPoints.length === 3) {
45222 _lastPoints.unshift(_lastPoints[0]);
45224 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45225 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45226 _lastPoints.shift();
45232 calculateCurveWidths: function (startPoint, endPoint) {
45233 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45234 (1 - this.velocity_filter_weight) * this._lastVelocity;
45236 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45239 start: this._lastWidth
45242 this._lastVelocity = velocity;
45243 this._lastWidth = newWidth;
45247 drawDot: function (_a) {
45248 var color = _a.color, point = _a.point;
45249 var ctx = this.canvasElCtx();
45250 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45252 this.drawCurveSegment(point.x, point.y, width);
45254 ctx.fillStyle = color;
45258 drawCurve: function (_a) {
45259 var color = _a.color, curve = _a.curve;
45260 var ctx = this.canvasElCtx();
45261 var widthDelta = curve.endWidth - curve.startWidth;
45262 var drawSteps = Math.floor(curve.length()) * 2;
45264 ctx.fillStyle = color;
45265 for (var i = 0; i < drawSteps; i += 1) {
45266 var t = i / drawSteps;
45272 var x = uuu * curve.startPoint.x;
45273 x += 3 * uu * t * curve.control1.x;
45274 x += 3 * u * tt * curve.control2.x;
45275 x += ttt * curve.endPoint.x;
45276 var y = uuu * curve.startPoint.y;
45277 y += 3 * uu * t * curve.control1.y;
45278 y += 3 * u * tt * curve.control2.y;
45279 y += ttt * curve.endPoint.y;
45280 var width = curve.startWidth + ttt * widthDelta;
45281 this.drawCurveSegment(x, y, width);
45287 drawCurveSegment: function (x, y, width) {
45288 var ctx = this.canvasElCtx();
45290 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45291 this.is_empty = false;
45296 var ctx = this.canvasElCtx();
45297 var canvas = this.canvasEl().dom;
45298 ctx.fillStyle = this.bg_color;
45299 ctx.clearRect(0, 0, canvas.width, canvas.height);
45300 ctx.fillRect(0, 0, canvas.width, canvas.height);
45301 this.curve_data = [];
45303 this.is_empty = true;
45308 return this.el.select('input',true).first();
45311 canvasEl: function()
45313 return this.el.select('canvas',true).first();
45316 canvasElCtx: function()
45318 return this.el.select('canvas',true).first().dom.getContext('2d');
45321 getImage: function(type)
45323 if(this.is_empty) {
45328 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45331 drawFromImage: function(img_src)
45333 var img = new Image();
45335 img.onload = function(){
45336 this.canvasElCtx().drawImage(img, 0, 0);
45341 this.is_empty = false;
45344 selectImage: function()
45346 this.fileEl().dom.click();
45349 uploadImage: function(e)
45351 var reader = new FileReader();
45353 reader.onload = function(e){
45354 var img = new Image();
45355 img.onload = function(){
45357 this.canvasElCtx().drawImage(img, 0, 0);
45359 img.src = e.target.result;
45362 reader.readAsDataURL(e.target.files[0]);
45365 // Bezier Point Constructor
45366 Point: (function () {
45367 function Point(x, y, time) {
45370 this.time = time || Date.now();
45372 Point.prototype.distanceTo = function (start) {
45373 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45375 Point.prototype.equals = function (other) {
45376 return this.x === other.x && this.y === other.y && this.time === other.time;
45378 Point.prototype.velocityFrom = function (start) {
45379 return this.time !== start.time
45380 ? this.distanceTo(start) / (this.time - start.time)
45387 // Bezier Constructor
45388 Bezier: (function () {
45389 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45390 this.startPoint = startPoint;
45391 this.control2 = control2;
45392 this.control1 = control1;
45393 this.endPoint = endPoint;
45394 this.startWidth = startWidth;
45395 this.endWidth = endWidth;
45397 Bezier.fromPoints = function (points, widths, scope) {
45398 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45399 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45400 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45402 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45403 var dx1 = s1.x - s2.x;
45404 var dy1 = s1.y - s2.y;
45405 var dx2 = s2.x - s3.x;
45406 var dy2 = s2.y - s3.y;
45407 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45408 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45409 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45410 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45411 var dxm = m1.x - m2.x;
45412 var dym = m1.y - m2.y;
45413 var k = l2 / (l1 + l2);
45414 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45415 var tx = s2.x - cm.x;
45416 var ty = s2.y - cm.y;
45418 c1: new scope.Point(m1.x + tx, m1.y + ty),
45419 c2: new scope.Point(m2.x + tx, m2.y + ty)
45422 Bezier.prototype.length = function () {
45427 for (var i = 0; i <= steps; i += 1) {
45429 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45430 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45432 var xdiff = cx - px;
45433 var ydiff = cy - py;
45434 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45441 Bezier.prototype.point = function (t, start, c1, c2, end) {
45442 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45443 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45444 + (3.0 * c2 * (1.0 - t) * t * t)
45445 + (end * t * t * t);
45450 throttleStroke: function(fn, wait) {
45451 if (wait === void 0) { wait = 250; }
45453 var timeout = null;
45457 var later = function () {
45458 previous = Date.now();
45460 result = fn.apply(storedContext, storedArgs);
45462 storedContext = null;
45466 return function wrapper() {
45468 for (var _i = 0; _i < arguments.length; _i++) {
45469 args[_i] = arguments[_i];
45471 var now = Date.now();
45472 var remaining = wait - (now - previous);
45473 storedContext = this;
45475 if (remaining <= 0 || remaining > wait) {
45477 clearTimeout(timeout);
45481 result = fn.apply(storedContext, storedArgs);
45483 storedContext = null;
45487 else if (!timeout) {
45488 timeout = window.setTimeout(later, remaining);