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 * @children Roo.bootstrap.Component
854 * Bootstrap Body class
858 * @param {Object} config The config object
861 Roo.bootstrap.Body = function(config){
863 config = config || {};
865 Roo.bootstrap.Body.superclass.constructor.call(this, config);
866 this.el = Roo.get(config.el ? config.el : document.body );
867 if (this.cls && this.cls.length) {
868 Roo.get(document.body).addClass(this.cls);
872 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
874 is_body : true,// just to make sure it's constructed?
879 onRender : function(ct, position)
881 /* Roo.log("Roo.bootstrap.Body - onRender");
882 if (this.cls && this.cls.length) {
883 Roo.get(document.body).addClass(this.cls);
902 * @class Roo.bootstrap.ButtonGroup
903 * @extends Roo.bootstrap.Component
904 * Bootstrap ButtonGroup class
905 * @cfg {String} size lg | sm | xs (default empty normal)
906 * @cfg {String} align vertical | justified (default none)
907 * @cfg {String} direction up | down (default down)
908 * @cfg {Boolean} toolbar false | true
909 * @cfg {Boolean} btn true | false
914 * @param {Object} config The config object
917 Roo.bootstrap.ButtonGroup = function(config){
918 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
921 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
929 getAutoCreate : function(){
935 cfg.html = this.html || cfg.html;
946 if (['vertical','justified'].indexOf(this.align)!==-1) {
947 cfg.cls = 'btn-group-' + this.align;
949 if (this.align == 'justified') {
950 console.log(this.items);
954 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
955 cfg.cls += ' btn-group-' + this.size;
958 if (this.direction == 'up') {
959 cfg.cls += ' dropup' ;
965 * Add a button to the group (similar to NavItem API.)
967 addItem : function(cfg)
969 var cn = new Roo.bootstrap.Button(cfg);
971 cn.parentId = this.id;
972 cn.onRender(this.el, null);
986 * @class Roo.bootstrap.Button
987 * @extends Roo.bootstrap.Component
988 * Bootstrap Button class
989 * @cfg {String} html The button content
990 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
991 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
992 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
993 * @cfg {String} size (lg|sm|xs)
994 * @cfg {String} tag (a|input|submit)
995 * @cfg {String} href empty or href
996 * @cfg {Boolean} disabled default false;
997 * @cfg {Boolean} isClose default false;
998 * @cfg {String} glyphicon depricated - use fa
999 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1000 * @cfg {String} badge text for badge
1001 * @cfg {String} theme (default|glow)
1002 * @cfg {Boolean} inverse dark themed version
1003 * @cfg {Boolean} toggle is it a slidy toggle button
1004 * @cfg {Boolean} pressed default null - if the button ahs active state
1005 * @cfg {String} ontext text for on slidy toggle state
1006 * @cfg {String} offtext text for off slidy toggle state
1007 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1008 * @cfg {Boolean} removeClass remove the standard class..
1009 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1010 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1013 * Create a new button
1014 * @param {Object} config The config object
1018 Roo.bootstrap.Button = function(config){
1019 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1025 * When a button is pressed
1026 * @param {Roo.bootstrap.Button} btn
1027 * @param {Roo.EventObject} e
1032 * When a button is double clicked
1033 * @param {Roo.bootstrap.Button} btn
1034 * @param {Roo.EventObject} e
1039 * After the button has been toggles
1040 * @param {Roo.bootstrap.Button} btn
1041 * @param {Roo.EventObject} e
1042 * @param {boolean} pressed (also available as button.pressed)
1048 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1069 preventDefault: true,
1078 getAutoCreate : function(){
1086 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1087 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1088 this.tag = 'button';
1092 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1094 if (this.toggle == true) {
1097 cls: 'slider-frame roo-button',
1101 'data-on-text':'ON',
1102 'data-off-text':'OFF',
1103 cls: 'slider-button',
1108 // why are we validating the weights?
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110 cfg.cls += ' ' + this.weight;
1117 cfg.cls += ' close';
1119 cfg["aria-hidden"] = true;
1121 cfg.html = "×";
1127 if (this.theme==='default') {
1128 cfg.cls = 'btn roo-button';
1130 //if (this.parentType != 'Navbar') {
1131 this.weight = this.weight.length ? this.weight : 'default';
1133 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1135 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1136 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1137 cfg.cls += ' btn-' + outline + weight;
1138 if (this.weight == 'default') {
1140 cfg.cls += ' btn-' + this.weight;
1143 } else if (this.theme==='glow') {
1146 cfg.cls = 'btn-glow roo-button';
1148 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1150 cfg.cls += ' ' + this.weight;
1156 this.cls += ' inverse';
1160 if (this.active || this.pressed === true) {
1161 cfg.cls += ' active';
1164 if (this.disabled) {
1165 cfg.disabled = 'disabled';
1169 Roo.log('changing to ul' );
1171 this.glyphicon = 'caret';
1172 if (Roo.bootstrap.version == 4) {
1173 this.fa = 'caret-down';
1178 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1180 //gsRoo.log(this.parentType);
1181 if (this.parentType === 'Navbar' && !this.parent().bar) {
1182 Roo.log('changing to li?');
1191 href : this.href || '#'
1194 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1195 cfg.cls += ' dropdown';
1202 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1204 if (this.glyphicon) {
1205 cfg.html = ' ' + cfg.html;
1210 cls: 'glyphicon glyphicon-' + this.glyphicon
1215 cfg.html = ' ' + cfg.html;
1220 cls: 'fa fas fa-' + this.fa
1230 // cfg.cls='btn roo-button';
1234 var value = cfg.html;
1239 cls: 'glyphicon glyphicon-' + this.glyphicon,
1246 cls: 'fa fas fa-' + this.fa,
1251 var bw = this.badge_weight.length ? this.badge_weight :
1252 (this.weight.length ? this.weight : 'secondary');
1253 bw = bw == 'default' ? 'secondary' : bw;
1259 cls: 'badge badge-' + bw,
1268 cfg.cls += ' dropdown';
1269 cfg.html = typeof(cfg.html) != 'undefined' ?
1270 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1273 if (cfg.tag !== 'a' && this.href !== '') {
1274 throw "Tag must be a to set href.";
1275 } else if (this.href.length > 0) {
1276 cfg.href = this.href;
1279 if(this.removeClass){
1284 cfg.target = this.target;
1289 initEvents: function() {
1290 // Roo.log('init events?');
1291 // Roo.log(this.el.dom);
1294 if (typeof (this.menu) != 'undefined') {
1295 this.menu.parentType = this.xtype;
1296 this.menu.triggerEl = this.el;
1297 this.addxtype(Roo.apply({}, this.menu));
1301 if (this.el.hasClass('roo-button')) {
1302 this.el.on('click', this.onClick, this);
1303 this.el.on('dblclick', this.onDblClick, this);
1305 this.el.select('.roo-button').on('click', this.onClick, this);
1306 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1310 if(this.removeClass){
1311 this.el.on('click', this.onClick, this);
1314 if (this.group === true) {
1315 if (this.pressed === false || this.pressed === true) {
1318 this.pressed = false;
1319 this.setActive(this.pressed);
1324 this.el.enableDisplayMode();
1327 onClick : function(e)
1329 if (this.disabled) {
1333 Roo.log('button on click ');
1334 if(this.preventDefault){
1343 this.setActive(true);
1344 var pi = this.parent().items;
1345 for (var i = 0;i < pi.length;i++) {
1346 if (this == pi[i]) {
1349 if (pi[i].el.hasClass('roo-button')) {
1350 pi[i].setActive(false);
1353 this.fireEvent('click', this, e);
1357 if (this.pressed === true || this.pressed === false) {
1358 this.toggleActive(e);
1362 this.fireEvent('click', this, e);
1364 onDblClick: function(e)
1366 if (this.disabled) {
1369 if(this.preventDefault){
1372 this.fireEvent('dblclick', this, e);
1375 * Enables this button
1379 this.disabled = false;
1380 this.el.removeClass('disabled');
1381 this.el.dom.removeAttribute("disabled");
1385 * Disable this button
1387 disable : function()
1389 this.disabled = true;
1390 this.el.addClass('disabled');
1391 this.el.attr("disabled", "disabled")
1394 * sets the active state on/off,
1395 * @param {Boolean} state (optional) Force a particular state
1397 setActive : function(v) {
1399 this.el[v ? 'addClass' : 'removeClass']('active');
1403 * toggles the current active state
1405 toggleActive : function(e)
1407 this.setActive(!this.pressed); // this modifies pressed...
1408 this.fireEvent('toggle', this, e, this.pressed);
1411 * get the current active state
1412 * @return {boolean} true if it's active
1414 isActive : function()
1416 return this.el.hasClass('active');
1419 * set the text of the first selected button
1421 setText : function(str)
1423 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1426 * get the text of the first selected button
1428 getText : function()
1430 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1433 setWeight : function(str)
1435 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1436 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1438 var outline = this.outline ? 'outline-' : '';
1439 if (str == 'default') {
1440 this.el.addClass('btn-default btn-outline-secondary');
1443 this.el.addClass('btn-' + outline + str);
1448 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1450 Roo.bootstrap.Button.weights = [
1470 * @class Roo.bootstrap.Column
1471 * @extends Roo.bootstrap.Component
1472 * Bootstrap Column class
1473 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1477 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1478 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1479 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1480 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1483 * @cfg {Boolean} hidden (true|false) hide the element
1484 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1485 * @cfg {String} fa (ban|check|...) font awesome icon
1486 * @cfg {Number} fasize (1|2|....) font awsome size
1488 * @cfg {String} icon (info-sign|check|...) glyphicon name
1490 * @cfg {String} html content of column.
1493 * Create a new Column
1494 * @param {Object} config The config object
1497 Roo.bootstrap.Column = function(config){
1498 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1501 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1519 getAutoCreate : function(){
1520 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1528 var sizes = ['xs','sm','md','lg'];
1529 sizes.map(function(size ,ix){
1530 //Roo.log( size + ':' + settings[size]);
1532 if (settings[size+'off'] !== false) {
1533 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1536 if (settings[size] === false) {
1540 if (!settings[size]) { // 0 = hidden
1541 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1543 for (var i = ix; i > -1; i--) {
1544 cfg.cls += ' d-' + sizes[i] + '-none';
1550 cfg.cls += ' col-' + size + '-' + settings[size] + (
1551 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1557 cfg.cls += ' hidden';
1560 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1561 cfg.cls +=' alert alert-' + this.alert;
1565 if (this.html.length) {
1566 cfg.html = this.html;
1570 if (this.fasize > 1) {
1571 fasize = ' fa-' + this.fasize + 'x';
1573 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1578 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1597 * @class Roo.bootstrap.Container
1598 * @extends Roo.bootstrap.Component
1600 * Bootstrap Container class
1601 * @cfg {Boolean} jumbotron is it a jumbotron element
1602 * @cfg {String} html content of element
1603 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1604 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1605 * @cfg {String} header content of header (for panel)
1606 * @cfg {String} footer content of footer (for panel)
1607 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1608 * @cfg {String} tag (header|aside|section) type of HTML tag.
1609 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1610 * @cfg {String} fa font awesome icon
1611 * @cfg {String} icon (info-sign|check|...) glyphicon name
1612 * @cfg {Boolean} hidden (true|false) hide the element
1613 * @cfg {Boolean} expandable (true|false) default false
1614 * @cfg {Boolean} expanded (true|false) default true
1615 * @cfg {String} rheader contet on the right of header
1616 * @cfg {Boolean} clickable (true|false) default false
1620 * Create a new Container
1621 * @param {Object} config The config object
1624 Roo.bootstrap.Container = function(config){
1625 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1631 * After the panel has been expand
1633 * @param {Roo.bootstrap.Container} this
1638 * After the panel has been collapsed
1640 * @param {Roo.bootstrap.Container} this
1645 * When a element is chick
1646 * @param {Roo.bootstrap.Container} this
1647 * @param {Roo.EventObject} e
1653 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1671 getChildContainer : function() {
1677 if (this.panel.length) {
1678 return this.el.select('.panel-body',true).first();
1685 getAutoCreate : function(){
1688 tag : this.tag || 'div',
1692 if (this.jumbotron) {
1693 cfg.cls = 'jumbotron';
1698 // - this is applied by the parent..
1700 // cfg.cls = this.cls + '';
1703 if (this.sticky.length) {
1705 var bd = Roo.get(document.body);
1706 if (!bd.hasClass('bootstrap-sticky')) {
1707 bd.addClass('bootstrap-sticky');
1708 Roo.select('html',true).setStyle('height', '100%');
1711 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1715 if (this.well.length) {
1716 switch (this.well) {
1719 cfg.cls +=' well well-' +this.well;
1728 cfg.cls += ' hidden';
1732 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1733 cfg.cls +=' alert alert-' + this.alert;
1738 if (this.panel.length) {
1739 cfg.cls += ' panel panel-' + this.panel;
1741 if (this.header.length) {
1745 if(this.expandable){
1747 cfg.cls = cfg.cls + ' expandable';
1751 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1759 cls : 'panel-title',
1760 html : (this.expandable ? ' ' : '') + this.header
1764 cls: 'panel-header-right',
1770 cls : 'panel-heading',
1771 style : this.expandable ? 'cursor: pointer' : '',
1779 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1784 if (this.footer.length) {
1786 cls : 'panel-footer',
1795 body.html = this.html || cfg.html;
1796 // prefix with the icons..
1798 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1801 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1806 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1807 cfg.cls = 'container';
1813 initEvents: function()
1815 if(this.expandable){
1816 var headerEl = this.headerEl();
1819 headerEl.on('click', this.onToggleClick, this);
1824 this.el.on('click', this.onClick, this);
1829 onToggleClick : function()
1831 var headerEl = this.headerEl();
1847 if(this.fireEvent('expand', this)) {
1849 this.expanded = true;
1851 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1853 this.el.select('.panel-body',true).first().removeClass('hide');
1855 var toggleEl = this.toggleEl();
1861 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1866 collapse : function()
1868 if(this.fireEvent('collapse', this)) {
1870 this.expanded = false;
1872 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1873 this.el.select('.panel-body',true).first().addClass('hide');
1875 var toggleEl = this.toggleEl();
1881 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1885 toggleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1891 return this.el.select('.panel-heading .fa',true).first();
1894 headerEl : function()
1896 if(!this.el || !this.panel.length || !this.header.length){
1900 return this.el.select('.panel-heading',true).first()
1905 if(!this.el || !this.panel.length){
1909 return this.el.select('.panel-body',true).first()
1912 titleEl : function()
1914 if(!this.el || !this.panel.length || !this.header.length){
1918 return this.el.select('.panel-title',true).first();
1921 setTitle : function(v)
1923 var titleEl = this.titleEl();
1929 titleEl.dom.innerHTML = v;
1932 getTitle : function()
1935 var titleEl = this.titleEl();
1941 return titleEl.dom.innerHTML;
1944 setRightTitle : function(v)
1946 var t = this.el.select('.panel-header-right',true).first();
1952 t.dom.innerHTML = v;
1955 onClick : function(e)
1959 this.fireEvent('click', this, e);
1966 * This is BS4's Card element.. - similar to our containers probably..
1970 * @class Roo.bootstrap.Card
1971 * @extends Roo.bootstrap.Component
1972 * Bootstrap Card class
1975 * possible... may not be implemented..
1976 * @cfg {String} header_image src url of image.
1977 * @cfg {String|Object} header
1978 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1979 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1981 * @cfg {String} title
1982 * @cfg {String} subtitle
1983 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1984 * @cfg {String} footer
1986 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1988 * @cfg {String} margin (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1990 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1991 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1992 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1993 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1994 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1996 * @cfg {String} padding (0|1|2|3|4|5)
1997 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1998 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1999 * @cfg {String} padding_left (0|1|2|3|4|5)
2000 * @cfg {String} padding_right (0|1|2|3|4|5)
2001 * @cfg {String} padding_x (0|1|2|3|4|5)
2002 * @cfg {String} padding_y (0|1|2|3|4|5)
2004 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2006 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2007 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2008 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010 * @config {Boolean} dragable if this card can be dragged.
2011 * @config {String} drag_group group for drag
2012 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2013 * @config {String} drop_group group for drag
2015 * @config {Boolean} collapsable can the body be collapsed.
2016 * @config {Boolean} collapsed is the body collapsed when rendered...
2017 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2018 * @config {Boolean} rotated is the body rotated when rendered...
2021 * Create a new Container
2022 * @param {Object} config The config object
2025 Roo.bootstrap.Card = function(config){
2026 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2032 * When a element a card is dropped
2033 * @param {Roo.bootstrap.Card} this
2036 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2037 * @param {String} position 'above' or 'below'
2038 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2044 * When a element a card is rotate
2045 * @param {Roo.bootstrap.Card} this
2046 * @param {Roo.Element} n the node being dropped?
2047 * @param {Boolean} rotate status
2052 * When a card element is dragged over ready to drop (return false to block dropable)
2053 * @param {Roo.bootstrap.Card} this
2054 * @param {Object} data from dragdrop
2062 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2067 margin: '', /// may be better in component?
2097 collapsable : false,
2106 childContainer : false,
2107 dropEl : false, /// the dom placeholde element that indicates drop location.
2108 containerEl: false, // body container
2109 bodyEl: false, // card-body
2110 headerContainerEl : false, //
2112 header_imageEl : false,
2115 layoutCls : function()
2119 Roo.log(this.margin_bottom.length);
2120 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2121 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2123 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2124 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2126 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2127 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2131 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2132 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2133 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2137 // more generic support?
2145 // Roo.log("Call onRender: " + this.xtype);
2146 /* We are looking at something like this.
2148 <img src="..." class="card-img-top" alt="...">
2149 <div class="card-body">
2150 <h5 class="card-title">Card title</h5>
2151 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2153 >> this bit is really the body...
2154 <div> << we will ad dthis in hopefully it will not break shit.
2156 ** card text does not actually have any styling...
2158 <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>
2161 <a href="#" class="card-link">Card link</a>
2164 <div class="card-footer">
2165 <small class="text-muted">Last updated 3 mins ago</small>
2169 getAutoCreate : function(){
2177 if (this.weight.length && this.weight != 'light') {
2178 cfg.cls += ' text-white';
2180 cfg.cls += ' text-dark'; // need as it's nested..
2182 if (this.weight.length) {
2183 cfg.cls += ' bg-' + this.weight;
2186 cfg.cls += ' ' + this.layoutCls();
2189 var hdr_ctr = false;
2190 if (this.header.length) {
2192 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2193 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2207 if (this.collapsable) {
2210 cls : 'd-block user-select-none',
2214 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2219 hdr.cn.push(hdr_ctr);
2224 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2229 if (this.header_image.length) {
2232 cls : 'card-img-top',
2233 src: this.header_image // escape?
2238 cls : 'card-img-top d-none'
2244 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2248 if (this.collapsable || this.rotateable) {
2251 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2258 if (this.title.length) {
2262 src: this.title // escape?
2266 if (this.subtitle.length) {
2270 src: this.subtitle // escape?
2276 cls : 'roo-card-body-ctr'
2279 if (this.html.length) {
2285 // fixme ? handle objects?
2287 if (this.footer.length) {
2290 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2295 cfg.cn.push({cls : 'card-footer d-none'});
2304 getCardHeader : function()
2306 var ret = this.el.select('.card-header',true).first();
2307 if (ret.hasClass('d-none')) {
2308 ret.removeClass('d-none');
2313 getCardFooter : function()
2315 var ret = this.el.select('.card-footer',true).first();
2316 if (ret.hasClass('d-none')) {
2317 ret.removeClass('d-none');
2322 getCardImageTop : function()
2324 var ret = this.header_imageEl;
2325 if (ret.hasClass('d-none')) {
2326 ret.removeClass('d-none');
2332 getChildContainer : function()
2338 return this.el.select('.roo-card-body-ctr',true).first();
2341 initEvents: function()
2343 this.bodyEl = this.el.select('.card-body',true).first();
2344 this.containerEl = this.getChildContainer();
2346 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2347 containerScroll: true,
2348 ddGroup: this.drag_group || 'default_card_drag_group'
2350 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2352 if (this.dropable) {
2353 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2354 containerScroll: true,
2355 ddGroup: this.drop_group || 'default_card_drag_group'
2357 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2358 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2359 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2360 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2361 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2364 if (this.collapsable) {
2365 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2367 if (this.rotateable) {
2368 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2370 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2372 this.footerEl = this.el.select('.card-footer',true).first();
2373 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2374 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2375 this.headerEl = this.el.select('.card-header',true).first();
2378 this.el.addClass('roo-card-rotated');
2379 this.fireEvent('rotate', this, true);
2381 this.header_imageEl = this.el.select('.card-img-top',true).first();
2382 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2385 getDragData : function(e)
2387 var target = this.getEl();
2389 //this.handleSelection(e);
2394 nodes: this.getEl(),
2399 dragData.ddel = target.dom ; // the div element
2400 Roo.log(target.getWidth( ));
2401 dragData.ddel.style.width = target.getWidth() + 'px';
2408 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2409 * whole Element becomes the target, and this causes the drop gesture to append.
2411 * Returns an object:
2414 position : 'below' or 'above'
2415 card : relateive to card OBJECT (or true for no cards listed)
2416 items_n : relative to nth item in list
2417 card_n : relative to nth card in list
2422 getTargetFromEvent : function(e, dragged_card_el)
2424 var target = e.getTarget();
2425 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2426 target = target.parentNode;
2437 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2438 // see if target is one of the 'cards'...
2441 //Roo.log(this.items.length);
2444 var last_card_n = 0;
2446 for (var i = 0;i< this.items.length;i++) {
2448 if (!this.items[i].el.hasClass('card')) {
2451 pos = this.getDropPoint(e, this.items[i].el.dom);
2453 cards_len = ret.cards.length;
2454 //Roo.log(this.items[i].el.dom.id);
2455 ret.cards.push(this.items[i]);
2457 if (ret.card_n < 0 && pos == 'above') {
2458 ret.position = cards_len > 0 ? 'below' : pos;
2459 ret.items_n = i > 0 ? i - 1 : 0;
2460 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2461 ret.card = ret.cards[ret.card_n];
2464 if (!ret.cards.length) {
2466 ret.position = 'below';
2470 // could not find a card.. stick it at the end..
2471 if (ret.card_n < 0) {
2472 ret.card_n = last_card_n;
2473 ret.card = ret.cards[last_card_n];
2474 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2475 ret.position = 'below';
2478 if (this.items[ret.items_n].el == dragged_card_el) {
2482 if (ret.position == 'below') {
2483 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2485 if (card_after && card_after.el == dragged_card_el) {
2492 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2494 if (card_before && card_before.el == dragged_card_el) {
2501 onNodeEnter : function(n, dd, e, data){
2504 onNodeOver : function(n, dd, e, data)
2507 var target_info = this.getTargetFromEvent(e,data.source.el);
2508 if (target_info === false) {
2509 this.dropPlaceHolder('hide');
2512 Roo.log(['getTargetFromEvent', target_info ]);
2515 if (this.fireEvent('cardover', this, [ data ]) === false) {
2519 this.dropPlaceHolder('show', target_info,data);
2523 onNodeOut : function(n, dd, e, data){
2524 this.dropPlaceHolder('hide');
2527 onNodeDrop : function(n, dd, e, data)
2530 // call drop - return false if
2532 // this could actually fail - if the Network drops..
2533 // we will ignore this at present..- client should probably reload
2534 // the whole set of cards if stuff like that fails.
2537 var info = this.getTargetFromEvent(e,data.source.el);
2538 if (info === false) {
2541 this.dropPlaceHolder('hide');
2545 this.acceptCard(data.source, info.position, info.card, info.items_n);
2549 firstChildCard : function()
2551 for (var i = 0;i< this.items.length;i++) {
2553 if (!this.items[i].el.hasClass('card')) {
2556 return this.items[i];
2558 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2563 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2565 acceptCard : function(move_card, position, next_to_card )
2567 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2571 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2573 move_card.parent().removeCard(move_card);
2576 var dom = move_card.el.dom;
2577 dom.style.width = ''; // clear with - which is set by drag.
2579 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2580 var cardel = next_to_card.el.dom;
2582 if (position == 'above' ) {
2583 cardel.parentNode.insertBefore(dom, cardel);
2584 } else if (cardel.nextSibling) {
2585 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2587 cardel.parentNode.append(dom);
2590 // card container???
2591 this.containerEl.dom.append(dom);
2594 //FIXME HANDLE card = true
2596 // add this to the correct place in items.
2598 // remove Card from items.
2601 if (this.items.length) {
2603 //Roo.log([info.items_n, info.position, this.items.length]);
2604 for (var i =0; i < this.items.length; i++) {
2605 if (i == to_items_n && position == 'above') {
2606 nitems.push(move_card);
2608 nitems.push(this.items[i]);
2609 if (i == to_items_n && position == 'below') {
2610 nitems.push(move_card);
2613 this.items = nitems;
2614 Roo.log(this.items);
2616 this.items.push(move_card);
2619 move_card.parentId = this.id;
2625 removeCard : function(c)
2627 this.items = this.items.filter(function(e) { return e != c });
2630 dom.parentNode.removeChild(dom);
2631 dom.style.width = ''; // clear with - which is set by drag.
2636 /** Decide whether to drop above or below a View node. */
2637 getDropPoint : function(e, n, dd)
2642 if (n == this.containerEl.dom) {
2645 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2646 var c = t + (b - t) / 2;
2647 var y = Roo.lib.Event.getPageY(e);
2654 onToggleCollapse : function(e)
2656 if (this.collapsed) {
2657 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2658 this.collapsableEl.addClass('show');
2659 this.collapsed = false;
2662 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2663 this.collapsableEl.removeClass('show');
2664 this.collapsed = true;
2669 onToggleRotate : function(e)
2671 this.collapsableEl.removeClass('show');
2672 this.footerEl.removeClass('d-none');
2673 this.el.removeClass('roo-card-rotated');
2674 this.el.removeClass('d-none');
2677 this.collapsableEl.addClass('show');
2678 this.rotated = false;
2679 this.fireEvent('rotate', this, this.rotated);
2682 this.el.addClass('roo-card-rotated');
2683 this.footerEl.addClass('d-none');
2684 this.el.select('.roo-collapsable').removeClass('show');
2686 this.rotated = true;
2687 this.fireEvent('rotate', this, this.rotated);
2691 dropPlaceHolder: function (action, info, data)
2693 if (this.dropEl === false) {
2694 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2698 this.dropEl.removeClass(['d-none', 'd-block']);
2699 if (action == 'hide') {
2701 this.dropEl.addClass('d-none');
2704 // FIXME - info.card == true!!!
2705 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2707 if (info.card !== true) {
2708 var cardel = info.card.el.dom;
2710 if (info.position == 'above') {
2711 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2712 } else if (cardel.nextSibling) {
2713 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2715 cardel.parentNode.append(this.dropEl.dom);
2718 // card container???
2719 this.containerEl.dom.append(this.dropEl.dom);
2722 this.dropEl.addClass('d-block roo-card-dropzone');
2724 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2731 setHeaderText: function(html)
2734 if (this.headerContainerEl) {
2735 this.headerContainerEl.dom.innerHTML = html;
2738 onHeaderImageLoad : function(ev, he)
2740 if (!this.header_image_fit_square) {
2744 var hw = he.naturalHeight / he.naturalWidth;
2747 //var w = he.dom.naturalWidth;
2750 he.style.position = 'relative';
2752 var nw = (ww * (1/hw));
2753 Roo.get(he).setSize( ww * (1/hw), ww);
2754 he.style.left = ((ww - nw)/ 2) + 'px';
2755 he.style.position = 'relative';
2766 * Card header - holder for the card header elements.
2771 * @class Roo.bootstrap.CardHeader
2772 * @extends Roo.bootstrap.Element
2773 * Bootstrap CardHeader class
2775 * Create a new Card Header - that you can embed children into
2776 * @param {Object} config The config object
2779 Roo.bootstrap.CardHeader = function(config){
2780 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2783 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2786 container_method : 'getCardHeader'
2799 * Card footer - holder for the card footer elements.
2804 * @class Roo.bootstrap.CardFooter
2805 * @extends Roo.bootstrap.Element
2806 * Bootstrap CardFooter class
2808 * Create a new Card Footer - that you can embed children into
2809 * @param {Object} config The config object
2812 Roo.bootstrap.CardFooter = function(config){
2813 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2816 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2819 container_method : 'getCardFooter'
2832 * Card header - holder for the card header elements.
2837 * @class Roo.bootstrap.CardImageTop
2838 * @extends Roo.bootstrap.Element
2839 * Bootstrap CardImageTop class
2841 * Create a new Card Image Top container
2842 * @param {Object} config The config object
2845 Roo.bootstrap.CardImageTop = function(config){
2846 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2849 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2852 container_method : 'getCardImageTop'
2867 * @class Roo.bootstrap.ButtonUploader
2868 * @extends Roo.bootstrap.Button
2869 * Bootstrap Button Uploader class - it's a button which when you add files to it
2872 * @cfg {Number} errorTimeout default 3000
2873 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2874 * @cfg {Array} html The button text.
2875 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2878 * Create a new CardUploader
2879 * @param {Object} config The config object
2882 Roo.bootstrap.ButtonUploader = function(config){
2886 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2892 * @event beforeselect
2893 * When button is pressed, before show upload files dialog is shown
2894 * @param {Roo.bootstrap.UploaderButton} this
2897 'beforeselect' : true,
2899 * @event fired when files have been selected,
2900 * When a the download link is clicked
2901 * @param {Roo.bootstrap.UploaderButton} this
2902 * @param {Array} Array of files that have been uploaded
2909 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2912 errorTimeout : 3000,
2916 fileCollection : false,
2921 getAutoCreate : function()
2926 cls : 'd-none roo-card-upload-selector'
2929 if (this.multiple) {
2930 im.multiple = 'multiple';
2936 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2946 initEvents : function()
2949 Roo.bootstrap.Button.prototype.initEvents.call(this);
2955 this.urlAPI = (window.createObjectURL && window) ||
2956 (window.URL && URL.revokeObjectURL && URL) ||
2957 (window.webkitURL && webkitURL);
2962 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2964 this.selectorEl.on('change', this.onFileSelected, this);
2971 onClick : function(e)
2975 if ( this.fireEvent('beforeselect', this) === false) {
2979 this.selectorEl.dom.click();
2983 onFileSelected : function(e)
2987 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2990 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2991 this.selectorEl.dom.value = '';// hopefully reset..
2993 this.fireEvent('uploaded', this, files );
3001 * addCard - add an Attachment to the uploader
3002 * @param data - the data about the image to upload
3006 title : "Title of file",
3007 is_uploaded : false,
3008 src : "http://.....",
3009 srcfile : { the File upload object },
3010 mimetype : file.type,
3013 .. any other data...
3038 * @class Roo.bootstrap.Img
3039 * @extends Roo.bootstrap.Component
3040 * Bootstrap Img class
3041 * @cfg {Boolean} imgResponsive false | true
3042 * @cfg {String} border rounded | circle | thumbnail
3043 * @cfg {String} src image source
3044 * @cfg {String} alt image alternative text
3045 * @cfg {String} href a tag href
3046 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3047 * @cfg {String} xsUrl xs image source
3048 * @cfg {String} smUrl sm image source
3049 * @cfg {String} mdUrl md image source
3050 * @cfg {String} lgUrl lg image source
3051 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3054 * Create a new Input
3055 * @param {Object} config The config object
3058 Roo.bootstrap.Img = function(config){
3059 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3065 * The img click event for the img.
3066 * @param {Roo.EventObject} e
3071 * The when any image loads
3072 * @param {Roo.EventObject} e
3078 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3080 imgResponsive: true,
3089 backgroundContain : false,
3091 getAutoCreate : function()
3093 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3094 return this.createSingleImg();
3099 cls: 'roo-image-responsive-group',
3104 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3106 if(!_this[size + 'Url']){
3112 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3113 html: _this.html || cfg.html,
3114 src: _this[size + 'Url']
3117 img.cls += ' roo-image-responsive-' + size;
3119 var s = ['xs', 'sm', 'md', 'lg'];
3121 s.splice(s.indexOf(size), 1);
3123 Roo.each(s, function(ss){
3124 img.cls += ' hidden-' + ss;
3127 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3128 cfg.cls += ' img-' + _this.border;
3132 cfg.alt = _this.alt;
3145 a.target = _this.target;
3149 cfg.cn.push((_this.href) ? a : img);
3156 createSingleImg : function()
3160 cls: (this.imgResponsive) ? 'img-responsive' : '',
3162 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3165 if (this.backgroundContain) {
3166 cfg.cls += ' background-contain';
3169 cfg.html = this.html || cfg.html;
3171 if (this.backgroundContain) {
3172 cfg.style="background-image: url(" + this.src + ')';
3174 cfg.src = this.src || cfg.src;
3177 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3178 cfg.cls += ' img-' + this.border;
3195 a.target = this.target;
3200 return (this.href) ? a : cfg;
3203 initEvents: function()
3206 this.el.on('click', this.onClick, this);
3208 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3209 this.el.on('load', this.onImageLoad, this);
3211 // not sure if this works.. not tested
3212 this.el.select('img', true).on('load', this.onImageLoad, this);
3217 onClick : function(e)
3219 Roo.log('img onclick');
3220 this.fireEvent('click', this, e);
3222 onImageLoad: function(e)
3224 Roo.log('img load');
3225 this.fireEvent('load', this, e);
3229 * Sets the url of the image - used to update it
3230 * @param {String} url the url of the image
3233 setSrc : function(url)
3237 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3238 if (this.backgroundContain) {
3239 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3241 this.el.dom.src = url;
3246 this.el.select('img', true).first().dom.src = url;
3262 * @class Roo.bootstrap.Link
3263 * @extends Roo.bootstrap.Component
3264 * Bootstrap Link Class
3265 * @cfg {String} alt image alternative text
3266 * @cfg {String} href a tag href
3267 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3268 * @cfg {String} html the content of the link.
3269 * @cfg {String} anchor name for the anchor link
3270 * @cfg {String} fa - favicon
3272 * @cfg {Boolean} preventDefault (true | false) default false
3276 * Create a new Input
3277 * @param {Object} config The config object
3280 Roo.bootstrap.Link = function(config){
3281 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3287 * The img click event for the img.
3288 * @param {Roo.EventObject} e
3294 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3298 preventDefault: false,
3304 getAutoCreate : function()
3306 var html = this.html || '';
3308 if (this.fa !== false) {
3309 html = '<i class="fa fa-' + this.fa + '"></i>';
3314 // anchor's do not require html/href...
3315 if (this.anchor === false) {
3317 cfg.href = this.href || '#';
3319 cfg.name = this.anchor;
3320 if (this.html !== false || this.fa !== false) {
3323 if (this.href !== false) {
3324 cfg.href = this.href;
3328 if(this.alt !== false){
3333 if(this.target !== false) {
3334 cfg.target = this.target;
3340 initEvents: function() {
3342 if(!this.href || this.preventDefault){
3343 this.el.on('click', this.onClick, this);
3347 onClick : function(e)
3349 if(this.preventDefault){
3352 //Roo.log('img onclick');
3353 this.fireEvent('click', this, e);
3366 * @class Roo.bootstrap.Header
3367 * @extends Roo.bootstrap.Component
3368 * Bootstrap Header class
3369 * @cfg {String} html content of header
3370 * @cfg {Number} level (1|2|3|4|5|6) default 1
3373 * Create a new Header
3374 * @param {Object} config The config object
3378 Roo.bootstrap.Header = function(config){
3379 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3382 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3390 getAutoCreate : function(){
3395 tag: 'h' + (1 *this.level),
3396 html: this.html || ''
3408 * Ext JS Library 1.1.1
3409 * Copyright(c) 2006-2007, Ext JS, LLC.
3411 * Originally Released Under LGPL - original licence link has changed is not relivant.
3414 * <script type="text/javascript">
3418 * @class Roo.bootstrap.MenuMgr
3419 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3422 Roo.bootstrap.MenuMgr = function(){
3423 var menus, active, groups = {}, attached = false, lastShow = new Date();
3425 // private - called when first menu is created
3428 active = new Roo.util.MixedCollection();
3429 Roo.get(document).addKeyListener(27, function(){
3430 if(active.length > 0){
3438 if(active && active.length > 0){
3439 var c = active.clone();
3449 if(active.length < 1){
3450 Roo.get(document).un("mouseup", onMouseDown);
3458 var last = active.last();
3459 lastShow = new Date();
3462 Roo.get(document).on("mouseup", onMouseDown);
3467 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3468 m.parentMenu.activeChild = m;
3469 }else if(last && last.isVisible()){
3470 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3475 function onBeforeHide(m){
3477 m.activeChild.hide();
3479 if(m.autoHideTimer){
3480 clearTimeout(m.autoHideTimer);
3481 delete m.autoHideTimer;
3486 function onBeforeShow(m){
3487 var pm = m.parentMenu;
3488 if(!pm && !m.allowOtherMenus){
3490 }else if(pm && pm.activeChild && active != m){
3491 pm.activeChild.hide();
3495 // private this should really trigger on mouseup..
3496 function onMouseDown(e){
3497 Roo.log("on Mouse Up");
3499 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3500 Roo.log("MenuManager hideAll");
3509 function onBeforeCheck(mi, state){
3511 var g = groups[mi.group];
3512 for(var i = 0, l = g.length; i < l; i++){
3514 g[i].setChecked(false);
3523 * Hides all menus that are currently visible
3525 hideAll : function(){
3530 register : function(menu){
3534 menus[menu.id] = menu;
3535 menu.on("beforehide", onBeforeHide);
3536 menu.on("hide", onHide);
3537 menu.on("beforeshow", onBeforeShow);
3538 menu.on("show", onShow);
3540 if(g && menu.events["checkchange"]){
3544 groups[g].push(menu);
3545 menu.on("checkchange", onCheck);
3550 * Returns a {@link Roo.menu.Menu} object
3551 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3552 * be used to generate and return a new Menu instance.
3554 get : function(menu){
3555 if(typeof menu == "string"){ // menu id
3557 }else if(menu.events){ // menu instance
3560 /*else if(typeof menu.length == 'number'){ // array of menu items?
3561 return new Roo.bootstrap.Menu({items:menu});
3562 }else{ // otherwise, must be a config
3563 return new Roo.bootstrap.Menu(menu);
3570 unregister : function(menu){
3571 delete menus[menu.id];
3572 menu.un("beforehide", onBeforeHide);
3573 menu.un("hide", onHide);
3574 menu.un("beforeshow", onBeforeShow);
3575 menu.un("show", onShow);
3577 if(g && menu.events["checkchange"]){
3578 groups[g].remove(menu);
3579 menu.un("checkchange", onCheck);
3584 registerCheckable : function(menuItem){
3585 var g = menuItem.group;
3590 groups[g].push(menuItem);
3591 menuItem.on("beforecheckchange", onBeforeCheck);
3596 unregisterCheckable : function(menuItem){
3597 var g = menuItem.group;
3599 groups[g].remove(menuItem);
3600 menuItem.un("beforecheckchange", onBeforeCheck);
3612 * @class Roo.bootstrap.Menu
3613 * @extends Roo.bootstrap.Component
3614 * Bootstrap Menu class - container for MenuItems
3615 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3616 * @cfg {bool} hidden if the menu should be hidden when rendered.
3617 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3618 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3619 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3620 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3624 * @param {Object} config The config object
3628 Roo.bootstrap.Menu = function(config){
3630 if (config.type == 'treeview') {
3631 // normally menu's are drawn attached to the document to handle layering etc..
3632 // however treeview (used by the docs menu is drawn into the parent element)
3633 this.container_method = 'getChildContainer';
3636 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3637 if (this.registerMenu && this.type != 'treeview') {
3638 Roo.bootstrap.MenuMgr.register(this);
3645 * Fires before this menu is displayed (return false to block)
3646 * @param {Roo.menu.Menu} this
3651 * Fires before this menu is hidden (return false to block)
3652 * @param {Roo.menu.Menu} this
3657 * Fires after this menu is displayed
3658 * @param {Roo.menu.Menu} this
3663 * Fires after this menu is hidden
3664 * @param {Roo.menu.Menu} this
3669 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3670 * @param {Roo.menu.Menu} this
3671 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3672 * @param {Roo.EventObject} e
3677 * Fires when the mouse is hovering over this menu
3678 * @param {Roo.menu.Menu} this
3679 * @param {Roo.EventObject} e
3680 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3685 * Fires when the mouse exits this menu
3686 * @param {Roo.menu.Menu} this
3687 * @param {Roo.EventObject} e
3688 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3693 * Fires when a menu item contained in this menu is clicked
3694 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3695 * @param {Roo.EventObject} e
3699 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3702 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3706 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3709 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3711 registerMenu : true,
3713 menuItems :false, // stores the menu items..
3723 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3725 hideTrigger : false,
3730 getChildContainer : function() {
3734 getAutoCreate : function(){
3736 //if (['right'].indexOf(this.align)!==-1) {
3737 // cfg.cn[1].cls += ' pull-right'
3742 cls : 'dropdown-menu shadow' ,
3743 style : 'z-index:1000'
3747 if (this.type === 'submenu') {
3748 cfg.cls = 'submenu active';
3750 if (this.type === 'treeview') {
3751 cfg.cls = 'treeview-menu';
3756 initEvents : function() {
3758 // Roo.log("ADD event");
3759 // Roo.log(this.triggerEl.dom);
3760 if (this.triggerEl) {
3762 this.triggerEl.on('click', this.onTriggerClick, this);
3764 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3766 if (!this.hideTrigger) {
3767 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3768 // dropdown toggle on the 'a' in BS4?
3769 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3771 this.triggerEl.addClass('dropdown-toggle');
3777 this.el.on('touchstart' , this.onTouch, this);
3779 this.el.on('click' , this.onClick, this);
3781 this.el.on("mouseover", this.onMouseOver, this);
3782 this.el.on("mouseout", this.onMouseOut, this);
3786 findTargetItem : function(e)
3788 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3792 //Roo.log(t); Roo.log(t.id);
3794 //Roo.log(this.menuitems);
3795 return this.menuitems.get(t.id);
3797 //return this.items.get(t.menuItemId);
3803 onTouch : function(e)
3805 Roo.log("menu.onTouch");
3806 //e.stopEvent(); this make the user popdown broken
3810 onClick : function(e)
3812 Roo.log("menu.onClick");
3814 var t = this.findTargetItem(e);
3815 if(!t || t.isContainer){
3820 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3821 if(t == this.activeItem && t.shouldDeactivate(e)){
3822 this.activeItem.deactivate();
3823 delete this.activeItem;
3827 this.setActiveItem(t, true);
3835 Roo.log('pass click event');
3839 this.fireEvent("click", this, t, e);
3843 if(!t.href.length || t.href == '#'){
3844 (function() { _this.hide(); }).defer(100);
3849 onMouseOver : function(e){
3850 var t = this.findTargetItem(e);
3853 // if(t.canActivate && !t.disabled){
3854 // this.setActiveItem(t, true);
3858 this.fireEvent("mouseover", this, e, t);
3860 isVisible : function(){
3861 return !this.hidden;
3863 onMouseOut : function(e){
3864 var t = this.findTargetItem(e);
3867 // if(t == this.activeItem && t.shouldDeactivate(e)){
3868 // this.activeItem.deactivate();
3869 // delete this.activeItem;
3872 this.fireEvent("mouseout", this, e, t);
3877 * Displays this menu relative to another element
3878 * @param {String/HTMLElement/Roo.Element} element The element to align to
3879 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3880 * the element (defaults to this.defaultAlign)
3881 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3883 show : function(el, pos, parentMenu)
3885 if (false === this.fireEvent("beforeshow", this)) {
3886 Roo.log("show canceled");
3889 this.parentMenu = parentMenu;
3893 this.el.addClass('show'); // show otherwise we do not know how big we are..
3895 var xy = this.el.getAlignToXY(el, pos);
3897 // bl-tl << left align below
3898 // tl-bl << left align
3900 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3901 // if it goes to far to the right.. -> align left.
3902 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3905 // was left align - go right?
3906 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3909 // goes down the bottom
3910 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3912 var a = this.align.replace('?', '').split('-');
3913 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3917 this.showAt( xy , parentMenu, false);
3920 * Displays this menu at a specific xy position
3921 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3922 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3924 showAt : function(xy, parentMenu, /* private: */_e){
3925 this.parentMenu = parentMenu;
3930 this.fireEvent("beforeshow", this);
3931 //xy = this.el.adjustForConstraints(xy);
3935 this.hideMenuItems();
3936 this.hidden = false;
3937 if (this.triggerEl) {
3938 this.triggerEl.addClass('open');
3941 this.el.addClass('show');
3945 // reassign x when hitting right
3947 // reassign y when hitting bottom
3949 // but the list may align on trigger left or trigger top... should it be a properity?
3951 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3956 this.fireEvent("show", this);
3962 this.doFocus.defer(50, this);
3966 doFocus : function(){
3968 this.focusEl.focus();
3973 * Hides this menu and optionally all parent menus
3974 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3976 hide : function(deep)
3978 if (false === this.fireEvent("beforehide", this)) {
3979 Roo.log("hide canceled");
3982 this.hideMenuItems();
3983 if(this.el && this.isVisible()){
3985 if(this.activeItem){
3986 this.activeItem.deactivate();
3987 this.activeItem = null;
3989 if (this.triggerEl) {
3990 this.triggerEl.removeClass('open');
3993 this.el.removeClass('show');
3995 this.fireEvent("hide", this);
3997 if(deep === true && this.parentMenu){
3998 this.parentMenu.hide(true);
4002 onTriggerClick : function(e)
4004 Roo.log('trigger click');
4006 var target = e.getTarget();
4008 Roo.log(target.nodeName.toLowerCase());
4010 if(target.nodeName.toLowerCase() === 'i'){
4016 onTriggerPress : function(e)
4018 Roo.log('trigger press');
4019 //Roo.log(e.getTarget());
4020 // Roo.log(this.triggerEl.dom);
4022 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4023 var pel = Roo.get(e.getTarget());
4024 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4025 Roo.log('is treeview or dropdown?');
4029 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4033 if (this.isVisible()) {
4039 this.show(this.triggerEl, this.align, false);
4042 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049 hideMenuItems : function()
4051 Roo.log("hide Menu Items");
4056 this.el.select('.open',true).each(function(aa) {
4058 aa.removeClass('open');
4062 addxtypeChild : function (tree, cntr) {
4063 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4065 this.menuitems.add(comp);
4077 this.getEl().dom.innerHTML = '';
4078 this.menuitems.clear();
4092 * @class Roo.bootstrap.MenuItem
4093 * @extends Roo.bootstrap.Component
4094 * Bootstrap MenuItem class
4095 * @cfg {String} html the menu label
4096 * @cfg {String} href the link
4097 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4098 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4099 * @cfg {Boolean} active used on sidebars to highlight active itesm
4100 * @cfg {String} fa favicon to show on left of menu item.
4101 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4105 * Create a new MenuItem
4106 * @param {Object} config The config object
4110 Roo.bootstrap.MenuItem = function(config){
4111 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4116 * The raw click event for the entire grid.
4117 * @param {Roo.bootstrap.MenuItem} this
4118 * @param {Roo.EventObject} e
4124 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4128 preventDefault: false,
4129 isContainer : false,
4133 getAutoCreate : function(){
4135 if(this.isContainer){
4138 cls: 'dropdown-menu-item '
4148 cls : 'dropdown-item',
4153 if (this.fa !== false) {
4156 cls : 'fa fa-' + this.fa
4165 cls: 'dropdown-menu-item',
4168 if (this.parent().type == 'treeview') {
4169 cfg.cls = 'treeview-menu';
4172 cfg.cls += ' active';
4177 anc.href = this.href || cfg.cn[0].href ;
4178 ctag.html = this.html || cfg.cn[0].html ;
4182 initEvents: function()
4184 if (this.parent().type == 'treeview') {
4185 this.el.select('a').on('click', this.onClick, this);
4189 this.menu.parentType = this.xtype;
4190 this.menu.triggerEl = this.el;
4191 this.menu = this.addxtype(Roo.apply({}, this.menu));
4195 onClick : function(e)
4197 Roo.log('item on click ');
4199 if(this.preventDefault){
4202 //this.parent().hideMenuItems();
4204 this.fireEvent('click', this, e);
4223 * @class Roo.bootstrap.MenuSeparator
4224 * @extends Roo.bootstrap.Component
4225 * Bootstrap MenuSeparator class
4228 * Create a new MenuItem
4229 * @param {Object} config The config object
4233 Roo.bootstrap.MenuSeparator = function(config){
4234 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4237 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4239 getAutoCreate : function(){
4258 * @class Roo.bootstrap.Modal
4259 * @extends Roo.bootstrap.Component
4262 * Bootstrap Modal class
4263 * @cfg {String} title Title of dialog
4264 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4265 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4266 * @cfg {Boolean} specificTitle default false
4267 * @cfg {Array} buttons Array of buttons or standard button set..
4268 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4269 * @cfg {Boolean} animate default true
4270 * @cfg {Boolean} allow_close default true
4271 * @cfg {Boolean} fitwindow default false
4272 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4273 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4274 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4275 * @cfg {String} size (sm|lg|xl) default empty
4276 * @cfg {Number} max_width set the max width of modal
4277 * @cfg {Boolean} editableTitle can the title be edited
4282 * Create a new Modal Dialog
4283 * @param {Object} config The config object
4286 Roo.bootstrap.Modal = function(config){
4287 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4292 * The raw btnclick event for the button
4293 * @param {Roo.EventObject} e
4298 * Fire when dialog resize
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} e
4304 * @event titlechanged
4305 * Fire when the editable title has been changed
4306 * @param {Roo.bootstrap.Modal} this
4307 * @param {Roo.EventObject} value
4309 "titlechanged" : true
4312 this.buttons = this.buttons || [];
4315 this.tmpl = Roo.factory(this.tmpl);
4320 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4322 title : 'test dialog',
4332 specificTitle: false,
4334 buttonPosition: 'right',
4356 editableTitle : false,
4358 onRender : function(ct, position)
4360 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4363 var cfg = Roo.apply({}, this.getAutoCreate());
4366 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368 //if (!cfg.name.length) {
4372 cfg.cls += ' ' + this.cls;
4375 cfg.style = this.style;
4377 this.el = Roo.get(document.body).createChild(cfg, position);
4379 //var type = this.el.dom.type;
4382 if(this.tabIndex !== undefined){
4383 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4386 this.dialogEl = this.el.select('.modal-dialog',true).first();
4387 this.bodyEl = this.el.select('.modal-body',true).first();
4388 this.closeEl = this.el.select('.modal-header .close', true).first();
4389 this.headerEl = this.el.select('.modal-header',true).first();
4390 this.titleEl = this.el.select('.modal-title',true).first();
4391 this.footerEl = this.el.select('.modal-footer',true).first();
4393 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395 //this.el.addClass("x-dlg-modal");
4397 if (this.buttons.length) {
4398 Roo.each(this.buttons, function(bb) {
4399 var b = Roo.apply({}, bb);
4400 b.xns = b.xns || Roo.bootstrap;
4401 b.xtype = b.xtype || 'Button';
4402 if (typeof(b.listeners) == 'undefined') {
4403 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4406 var btn = Roo.factory(b);
4408 btn.render(this.getButtonContainer());
4412 // render the children.
4415 if(typeof(this.items) != 'undefined'){
4416 var items = this.items;
4419 for(var i =0;i < items.length;i++) {
4420 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4424 this.items = nitems;
4426 // where are these used - they used to be body/close/footer
4430 //this.el.addClass([this.fieldClass, this.cls]);
4434 getAutoCreate : function()
4436 // we will default to modal-body-overflow - might need to remove or make optional later.
4438 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4439 html : this.html || ''
4444 cls : 'modal-title',
4448 if(this.specificTitle){ // WTF is this?
4453 if (this.allow_close && Roo.bootstrap.version == 3) {
4463 if (this.editableTitle) {
4465 cls: 'form-control roo-editable-title d-none',
4471 if (this.allow_close && Roo.bootstrap.version == 4) {
4481 if(this.size.length){
4482 size = 'modal-' + this.size;
4485 var footer = Roo.bootstrap.version == 3 ?
4487 cls : 'modal-footer',
4491 cls: 'btn-' + this.buttonPosition
4496 { // BS4 uses mr-auto on left buttons....
4497 cls : 'modal-footer'
4508 cls: "modal-dialog " + size,
4511 cls : "modal-content",
4514 cls : 'modal-header',
4529 modal.cls += ' fade';
4535 getChildContainer : function() {
4540 getButtonContainer : function() {
4542 return Roo.bootstrap.version == 4 ?
4543 this.el.select('.modal-footer',true).first()
4544 : this.el.select('.modal-footer div',true).first();
4547 initEvents : function()
4549 if (this.allow_close) {
4550 this.closeEl.on('click', this.hide, this);
4552 Roo.EventManager.onWindowResize(this.resize, this, true);
4553 if (this.editableTitle) {
4554 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4555 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4556 this.headerEditEl.on('keyup', function(e) {
4557 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4558 this.toggleHeaderInput(false)
4561 this.headerEditEl.on('blur', function(e) {
4562 this.toggleHeaderInput(false)
4571 this.maskEl.setSize(
4572 Roo.lib.Dom.getViewWidth(true),
4573 Roo.lib.Dom.getViewHeight(true)
4576 if (this.fitwindow) {
4578 this.dialogEl.setStyle( { 'max-width' : '100%' });
4580 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4581 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4586 if(this.max_width !== 0) {
4588 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4591 this.setSize(w, this.height);
4595 if(this.max_height) {
4596 this.setSize(w,Math.min(
4598 Roo.lib.Dom.getViewportHeight(true) - 60
4604 if(!this.fit_content) {
4605 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4609 this.setSize(w, Math.min(
4611 this.headerEl.getHeight() +
4612 this.footerEl.getHeight() +
4613 this.getChildHeight(this.bodyEl.dom.childNodes),
4614 Roo.lib.Dom.getViewportHeight(true) - 60)
4620 setSize : function(w,h)
4631 if (!this.rendered) {
4634 this.toggleHeaderInput(false);
4635 //this.el.setStyle('display', 'block');
4636 this.el.removeClass('hideing');
4637 this.el.dom.style.display='block';
4639 Roo.get(document.body).addClass('modal-open');
4641 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4644 this.el.addClass('show');
4645 this.el.addClass('in');
4648 this.el.addClass('show');
4649 this.el.addClass('in');
4652 // not sure how we can show data in here..
4654 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4657 Roo.get(document.body).addClass("x-body-masked");
4659 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4660 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4661 this.maskEl.dom.style.display = 'block';
4662 this.maskEl.addClass('show');
4667 this.fireEvent('show', this);
4669 // set zindex here - otherwise it appears to be ignored...
4670 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4673 this.items.forEach( function(e) {
4674 e.layout ? e.layout() : false;
4682 if(this.fireEvent("beforehide", this) !== false){
4684 this.maskEl.removeClass('show');
4686 this.maskEl.dom.style.display = '';
4687 Roo.get(document.body).removeClass("x-body-masked");
4688 this.el.removeClass('in');
4689 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4691 if(this.animate){ // why
4692 this.el.addClass('hideing');
4693 this.el.removeClass('show');
4695 if (!this.el.hasClass('hideing')) {
4696 return; // it's been shown again...
4699 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4702 this.el.removeClass('hideing');
4706 this.el.removeClass('show');
4707 this.el.dom.style.display='';
4708 Roo.get(document.body).removeClass('modal-open');
4711 this.fireEvent('hide', this);
4714 isVisible : function()
4717 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4721 addButton : function(str, cb)
4725 var b = Roo.apply({}, { html : str } );
4726 b.xns = b.xns || Roo.bootstrap;
4727 b.xtype = b.xtype || 'Button';
4728 if (typeof(b.listeners) == 'undefined') {
4729 b.listeners = { click : cb.createDelegate(this) };
4732 var btn = Roo.factory(b);
4734 btn.render(this.getButtonContainer());
4740 setDefaultButton : function(btn)
4742 //this.el.select('.modal-footer').()
4745 resizeTo: function(w,h)
4747 this.dialogEl.setWidth(w);
4749 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4751 this.bodyEl.setHeight(h - diff);
4753 this.fireEvent('resize', this);
4756 setContentSize : function(w, h)
4760 onButtonClick: function(btn,e)
4763 this.fireEvent('btnclick', btn.name, e);
4766 * Set the title of the Dialog
4767 * @param {String} str new Title
4769 setTitle: function(str) {
4770 this.titleEl.dom.innerHTML = str;
4774 * Set the body of the Dialog
4775 * @param {String} str new Title
4777 setBody: function(str) {
4778 this.bodyEl.dom.innerHTML = str;
4781 * Set the body of the Dialog using the template
4782 * @param {Obj} data - apply this data to the template and replace the body contents.
4784 applyBody: function(obj)
4787 Roo.log("Error - using apply Body without a template");
4790 this.tmpl.overwrite(this.bodyEl, obj);
4793 getChildHeight : function(child_nodes)
4797 child_nodes.length == 0
4802 var child_height = 0;
4804 for(var i = 0; i < child_nodes.length; i++) {
4807 * for modal with tabs...
4808 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4810 var layout_childs = child_nodes[i].childNodes;
4812 for(var j = 0; j < layout_childs.length; j++) {
4814 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4816 var layout_body_childs = layout_childs[j].childNodes;
4818 for(var k = 0; k < layout_body_childs.length; k++) {
4820 if(layout_body_childs[k].classList.contains('navbar')) {
4821 child_height += layout_body_childs[k].offsetHeight;
4825 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4827 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4829 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4831 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4832 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4847 child_height += child_nodes[i].offsetHeight;
4848 // Roo.log(child_nodes[i].offsetHeight);
4851 return child_height;
4853 toggleHeaderInput : function(is_edit)
4855 if (!this.editableTitle) {
4856 return; // not editable.
4858 if (is_edit && this.is_header_editing) {
4859 return; // already editing..
4863 this.headerEditEl.dom.value = this.title;
4864 this.headerEditEl.removeClass('d-none');
4865 this.headerEditEl.dom.focus();
4866 this.titleEl.addClass('d-none');
4868 this.is_header_editing = true;
4871 // flip back to not editing.
4872 this.title = this.headerEditEl.dom.value;
4873 this.headerEditEl.addClass('d-none');
4874 this.titleEl.removeClass('d-none');
4875 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4876 this.is_header_editing = false;
4877 this.fireEvent('titlechanged', this, this.title);
4886 Roo.apply(Roo.bootstrap.Modal, {
4888 * Button config that displays a single OK button
4897 * Button config that displays Yes and No buttons
4913 * Button config that displays OK and Cancel buttons
4928 * Button config that displays Yes, No and Cancel buttons
4953 * messagebox - can be used as a replace
4957 * @class Roo.MessageBox
4958 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4962 Roo.Msg.alert('Status', 'Changes saved successfully.');
4964 // Prompt for user data:
4965 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4967 // process text value...
4971 // Show a dialog using config options:
4973 title:'Save Changes?',
4974 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4975 buttons: Roo.Msg.YESNOCANCEL,
4982 Roo.bootstrap.MessageBox = function(){
4983 var dlg, opt, mask, waitTimer;
4984 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4985 var buttons, activeTextEl, bwidth;
4989 var handleButton = function(button){
4991 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4995 var handleHide = function(){
4997 dlg.el.removeClass(opt.cls);
5000 // Roo.TaskMgr.stop(waitTimer);
5001 // waitTimer = null;
5006 var updateButtons = function(b){
5009 buttons["ok"].hide();
5010 buttons["cancel"].hide();
5011 buttons["yes"].hide();
5012 buttons["no"].hide();
5013 dlg.footerEl.hide();
5017 dlg.footerEl.show();
5018 for(var k in buttons){
5019 if(typeof buttons[k] != "function"){
5022 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5023 width += buttons[k].el.getWidth()+15;
5033 var handleEsc = function(d, k, e){
5034 if(opt && opt.closable !== false){
5044 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5045 * @return {Roo.BasicDialog} The BasicDialog element
5047 getDialog : function(){
5049 dlg = new Roo.bootstrap.Modal( {
5052 //constraintoviewport:false,
5054 //collapsible : false,
5059 //buttonAlign:"center",
5060 closeClick : function(){
5061 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5064 handleButton("cancel");
5069 dlg.on("hide", handleHide);
5071 //dlg.addKeyListener(27, handleEsc);
5073 this.buttons = buttons;
5074 var bt = this.buttonText;
5075 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5076 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5077 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5078 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5080 bodyEl = dlg.bodyEl.createChild({
5082 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5083 '<textarea class="roo-mb-textarea"></textarea>' +
5084 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5086 msgEl = bodyEl.dom.firstChild;
5087 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5088 textboxEl.enableDisplayMode();
5089 textboxEl.addKeyListener([10,13], function(){
5090 if(dlg.isVisible() && opt && opt.buttons){
5093 }else if(opt.buttons.yes){
5094 handleButton("yes");
5098 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5099 textareaEl.enableDisplayMode();
5100 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5101 progressEl.enableDisplayMode();
5103 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5104 var pf = progressEl.dom.firstChild;
5106 pp = Roo.get(pf.firstChild);
5107 pp.setHeight(pf.offsetHeight);
5115 * Updates the message box body text
5116 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5117 * the XHTML-compliant non-breaking space character '&#160;')
5118 * @return {Roo.MessageBox} This message box
5120 updateText : function(text)
5122 if(!dlg.isVisible() && !opt.width){
5123 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5124 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5126 msgEl.innerHTML = text || ' ';
5128 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5129 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5131 Math.min(opt.width || cw , this.maxWidth),
5132 Math.max(opt.minWidth || this.minWidth, bwidth)
5135 activeTextEl.setWidth(w);
5137 if(dlg.isVisible()){
5138 dlg.fixedcenter = false;
5140 // to big, make it scroll. = But as usual stupid IE does not support
5143 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5144 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5145 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5147 bodyEl.dom.style.height = '';
5148 bodyEl.dom.style.overflowY = '';
5151 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5153 bodyEl.dom.style.overflowX = '';
5156 dlg.setContentSize(w, bodyEl.getHeight());
5157 if(dlg.isVisible()){
5158 dlg.fixedcenter = true;
5164 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5165 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5166 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5167 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5168 * @return {Roo.MessageBox} This message box
5170 updateProgress : function(value, text){
5172 this.updateText(text);
5175 if (pp) { // weird bug on my firefox - for some reason this is not defined
5176 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5177 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5183 * Returns true if the message box is currently displayed
5184 * @return {Boolean} True if the message box is visible, else false
5186 isVisible : function(){
5187 return dlg && dlg.isVisible();
5191 * Hides the message box if it is displayed
5194 if(this.isVisible()){
5200 * Displays a new message box, or reinitializes an existing message box, based on the config options
5201 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5202 * The following config object properties are supported:
5204 Property Type Description
5205 ---------- --------------- ------------------------------------------------------------------------------------
5206 animEl String/Element An id or Element from which the message box should animate as it opens and
5207 closes (defaults to undefined)
5208 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5209 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5210 closable Boolean False to hide the top-right close button (defaults to true). Note that
5211 progress and wait dialogs will ignore this property and always hide the
5212 close button as they can only be closed programmatically.
5213 cls String A custom CSS class to apply to the message box element
5214 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5215 displayed (defaults to 75)
5216 fn Function A callback function to execute after closing the dialog. The arguments to the
5217 function will be btn (the name of the button that was clicked, if applicable,
5218 e.g. "ok"), and text (the value of the active text field, if applicable).
5219 Progress and wait dialogs will ignore this option since they do not respond to
5220 user actions and can only be closed programmatically, so any required function
5221 should be called by the same code after it closes the dialog.
5222 icon String A CSS class that provides a background image to be used as an icon for
5223 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5224 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5225 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5226 modal Boolean False to allow user interaction with the page while the message box is
5227 displayed (defaults to true)
5228 msg String A string that will replace the existing message box body text (defaults
5229 to the XHTML-compliant non-breaking space character ' ')
5230 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5231 progress Boolean True to display a progress bar (defaults to false)
5232 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5233 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5234 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5235 title String The title text
5236 value String The string value to set into the active textbox element if displayed
5237 wait Boolean True to display a progress bar (defaults to false)
5238 width Number The width of the dialog in pixels
5245 msg: 'Please enter your address:',
5247 buttons: Roo.MessageBox.OKCANCEL,
5250 animEl: 'addAddressBtn'
5253 * @param {Object} config Configuration options
5254 * @return {Roo.MessageBox} This message box
5256 show : function(options)
5259 // this causes nightmares if you show one dialog after another
5260 // especially on callbacks..
5262 if(this.isVisible()){
5265 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5266 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5267 Roo.log("New Dialog Message:" + options.msg )
5268 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5269 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5272 var d = this.getDialog();
5274 d.setTitle(opt.title || " ");
5275 d.closeEl.setDisplayed(opt.closable !== false);
5276 activeTextEl = textboxEl;
5277 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5282 textareaEl.setHeight(typeof opt.multiline == "number" ?
5283 opt.multiline : this.defaultTextHeight);
5284 activeTextEl = textareaEl;
5293 progressEl.setDisplayed(opt.progress === true);
5295 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5297 this.updateProgress(0);
5298 activeTextEl.dom.value = opt.value || "";
5300 dlg.setDefaultButton(activeTextEl);
5302 var bs = opt.buttons;
5306 }else if(bs && bs.yes){
5307 db = buttons["yes"];
5309 dlg.setDefaultButton(db);
5311 bwidth = updateButtons(opt.buttons);
5312 this.updateText(opt.msg);
5314 d.el.addClass(opt.cls);
5316 d.proxyDrag = opt.proxyDrag === true;
5317 d.modal = opt.modal !== false;
5318 d.mask = opt.modal !== false ? mask : false;
5320 // force it to the end of the z-index stack so it gets a cursor in FF
5321 document.body.appendChild(dlg.el.dom);
5322 d.animateTarget = null;
5323 d.show(options.animEl);
5329 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5330 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5331 * and closing the message box when the process is complete.
5332 * @param {String} title The title bar text
5333 * @param {String} msg The message box body text
5334 * @return {Roo.MessageBox} This message box
5336 progress : function(title, msg){
5343 minWidth: this.minProgressWidth,
5350 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5351 * If a callback function is passed it will be called after the user clicks the button, and the
5352 * id of the button that was clicked will be passed as the only parameter to the callback
5353 * (could also be the top-right close button).
5354 * @param {String} title The title bar text
5355 * @param {String} msg The message box body text
5356 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5357 * @param {Object} scope (optional) The scope of the callback function
5358 * @return {Roo.MessageBox} This message box
5360 alert : function(title, msg, fn, scope)
5375 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5376 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5377 * You are responsible for closing the message box when the process is complete.
5378 * @param {String} msg The message box body text
5379 * @param {String} title (optional) The title bar text
5380 * @return {Roo.MessageBox} This message box
5382 wait : function(msg, title){
5393 waitTimer = Roo.TaskMgr.start({
5395 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5403 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5404 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5405 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5406 * @param {String} title The title bar text
5407 * @param {String} msg The message box body text
5408 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5409 * @param {Object} scope (optional) The scope of the callback function
5410 * @return {Roo.MessageBox} This message box
5412 confirm : function(title, msg, fn, scope){
5416 buttons: this.YESNO,
5425 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5426 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5427 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5428 * (could also be the top-right close button) and the text that was entered will be passed as the two
5429 * parameters to the callback.
5430 * @param {String} title The title bar text
5431 * @param {String} msg The message box body text
5432 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5433 * @param {Object} scope (optional) The scope of the callback function
5434 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5435 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5436 * @return {Roo.MessageBox} This message box
5438 prompt : function(title, msg, fn, scope, multiline){
5442 buttons: this.OKCANCEL,
5447 multiline: multiline,
5454 * Button config that displays a single OK button
5459 * Button config that displays Yes and No buttons
5462 YESNO : {yes:true, no:true},
5464 * Button config that displays OK and Cancel buttons
5467 OKCANCEL : {ok:true, cancel:true},
5469 * Button config that displays Yes, No and Cancel buttons
5472 YESNOCANCEL : {yes:true, no:true, cancel:true},
5475 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5478 defaultTextHeight : 75,
5480 * The maximum width in pixels of the message box (defaults to 600)
5485 * The minimum width in pixels of the message box (defaults to 100)
5490 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5491 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5494 minProgressWidth : 250,
5496 * An object containing the default button text strings that can be overriden for localized language support.
5497 * Supported properties are: ok, cancel, yes and no.
5498 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5511 * Shorthand for {@link Roo.MessageBox}
5513 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5514 Roo.Msg = Roo.Msg || Roo.MessageBox;
5523 * @class Roo.bootstrap.Navbar
5524 * @extends Roo.bootstrap.Component
5525 * Bootstrap Navbar class
5528 * Create a new Navbar
5529 * @param {Object} config The config object
5533 Roo.bootstrap.Navbar = function(config){
5534 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5538 * @event beforetoggle
5539 * Fire before toggle the menu
5540 * @param {Roo.EventObject} e
5542 "beforetoggle" : true
5546 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5555 getAutoCreate : function(){
5558 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5562 initEvents :function ()
5564 //Roo.log(this.el.select('.navbar-toggle',true));
5565 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5572 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5574 var size = this.el.getSize();
5575 this.maskEl.setSize(size.width, size.height);
5576 this.maskEl.enableDisplayMode("block");
5585 getChildContainer : function()
5587 if (this.el && this.el.select('.collapse').getCount()) {
5588 return this.el.select('.collapse',true).first();
5603 onToggle : function()
5606 if(this.fireEvent('beforetoggle', this) === false){
5609 var ce = this.el.select('.navbar-collapse',true).first();
5611 if (!ce.hasClass('show')) {
5621 * Expand the navbar pulldown
5623 expand : function ()
5626 var ce = this.el.select('.navbar-collapse',true).first();
5627 if (ce.hasClass('collapsing')) {
5630 ce.dom.style.height = '';
5632 ce.addClass('in'); // old...
5633 ce.removeClass('collapse');
5634 ce.addClass('show');
5635 var h = ce.getHeight();
5637 ce.removeClass('show');
5638 // at this point we should be able to see it..
5639 ce.addClass('collapsing');
5641 ce.setHeight(0); // resize it ...
5642 ce.on('transitionend', function() {
5643 //Roo.log('done transition');
5644 ce.removeClass('collapsing');
5645 ce.addClass('show');
5646 ce.removeClass('collapse');
5648 ce.dom.style.height = '';
5649 }, this, { single: true} );
5651 ce.dom.scrollTop = 0;
5654 * Collapse the navbar pulldown
5656 collapse : function()
5658 var ce = this.el.select('.navbar-collapse',true).first();
5660 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5661 // it's collapsed or collapsing..
5664 ce.removeClass('in'); // old...
5665 ce.setHeight(ce.getHeight());
5666 ce.removeClass('show');
5667 ce.addClass('collapsing');
5669 ce.on('transitionend', function() {
5670 ce.dom.style.height = '';
5671 ce.removeClass('collapsing');
5672 ce.addClass('collapse');
5673 }, this, { single: true} );
5693 * @class Roo.bootstrap.NavSimplebar
5694 * @extends Roo.bootstrap.Navbar
5695 * Bootstrap Sidebar class
5697 * @cfg {Boolean} inverse is inverted color
5699 * @cfg {String} type (nav | pills | tabs)
5700 * @cfg {Boolean} arrangement stacked | justified
5701 * @cfg {String} align (left | right) alignment
5703 * @cfg {Boolean} main (true|false) main nav bar? default false
5704 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5706 * @cfg {String} tag (header|footer|nav|div) default is nav
5708 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5712 * Create a new Sidebar
5713 * @param {Object} config The config object
5717 Roo.bootstrap.NavSimplebar = function(config){
5718 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5721 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5737 getAutoCreate : function(){
5741 tag : this.tag || 'div',
5742 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5744 if (['light','white'].indexOf(this.weight) > -1) {
5745 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5747 cfg.cls += ' bg-' + this.weight;
5750 cfg.cls += ' navbar-inverse';
5754 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5756 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5765 cls: 'nav nav-' + this.xtype,
5771 this.type = this.type || 'nav';
5772 if (['tabs','pills'].indexOf(this.type) != -1) {
5773 cfg.cn[0].cls += ' nav-' + this.type
5777 if (this.type!=='nav') {
5778 Roo.log('nav type must be nav/tabs/pills')
5780 cfg.cn[0].cls += ' navbar-nav'
5786 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5787 cfg.cn[0].cls += ' nav-' + this.arrangement;
5791 if (this.align === 'right') {
5792 cfg.cn[0].cls += ' navbar-right';
5817 * navbar-expand-md fixed-top
5821 * @class Roo.bootstrap.NavHeaderbar
5822 * @extends Roo.bootstrap.NavSimplebar
5823 * Bootstrap Sidebar class
5825 * @cfg {String} brand what is brand
5826 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5827 * @cfg {String} brand_href href of the brand
5828 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5829 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5830 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5831 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5834 * Create a new Sidebar
5835 * @param {Object} config The config object
5839 Roo.bootstrap.NavHeaderbar = function(config){
5840 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5844 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5851 desktopCenter : false,
5854 getAutoCreate : function(){
5857 tag: this.nav || 'nav',
5858 cls: 'navbar navbar-expand-md',
5864 if (this.desktopCenter) {
5865 cn.push({cls : 'container', cn : []});
5873 cls: 'navbar-toggle navbar-toggler',
5874 'data-toggle': 'collapse',
5879 html: 'Toggle navigation'
5883 cls: 'icon-bar navbar-toggler-icon'
5896 cn.push( Roo.bootstrap.version == 4 ? btn : {
5898 cls: 'navbar-header',
5907 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5911 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5913 if (['light','white'].indexOf(this.weight) > -1) {
5914 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5916 cfg.cls += ' bg-' + this.weight;
5919 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5920 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5922 // tag can override this..
5924 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5927 if (this.brand !== '') {
5928 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5929 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5931 href: this.brand_href ? this.brand_href : '#',
5932 cls: 'navbar-brand',
5940 cfg.cls += ' main-nav';
5948 getHeaderChildContainer : function()
5950 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5951 return this.el.select('.navbar-header',true).first();
5954 return this.getChildContainer();
5957 getChildContainer : function()
5960 return this.el.select('.roo-navbar-collapse',true).first();
5965 initEvents : function()
5967 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5969 if (this.autohide) {
5974 Roo.get(document).on('scroll',function(e) {
5975 var ns = Roo.get(document).getScroll().top;
5976 var os = prevScroll;
5980 ft.removeClass('slideDown');
5981 ft.addClass('slideUp');
5984 ft.removeClass('slideUp');
5985 ft.addClass('slideDown');
6006 * @class Roo.bootstrap.NavSidebar
6007 * @extends Roo.bootstrap.Navbar
6008 * Bootstrap Sidebar class
6011 * Create a new Sidebar
6012 * @param {Object} config The config object
6016 Roo.bootstrap.NavSidebar = function(config){
6017 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6020 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6022 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6024 getAutoCreate : function(){
6029 cls: 'sidebar sidebar-nav'
6051 * @class Roo.bootstrap.NavGroup
6052 * @extends Roo.bootstrap.Component
6053 * Bootstrap NavGroup class
6054 * @cfg {String} align (left|right)
6055 * @cfg {Boolean} inverse
6056 * @cfg {String} type (nav|pills|tab) default nav
6057 * @cfg {String} navId - reference Id for navbar.
6058 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6061 * Create a new nav group
6062 * @param {Object} config The config object
6065 Roo.bootstrap.NavGroup = function(config){
6066 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6069 Roo.bootstrap.NavGroup.register(this);
6073 * Fires when the active item changes
6074 * @param {Roo.bootstrap.NavGroup} this
6075 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6076 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6083 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6095 getAutoCreate : function()
6097 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6103 if (Roo.bootstrap.version == 4) {
6104 if (['tabs','pills'].indexOf(this.type) != -1) {
6105 cfg.cls += ' nav-' + this.type;
6107 // trying to remove so header bar can right align top?
6108 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6109 // do not use on header bar...
6110 cfg.cls += ' navbar-nav';
6115 if (['tabs','pills'].indexOf(this.type) != -1) {
6116 cfg.cls += ' nav-' + this.type
6118 if (this.type !== 'nav') {
6119 Roo.log('nav type must be nav/tabs/pills')
6121 cfg.cls += ' navbar-nav'
6125 if (this.parent() && this.parent().sidebar) {
6128 cls: 'dashboard-menu sidebar-menu'
6134 if (this.form === true) {
6137 cls: 'navbar-form form-inline'
6139 //nav navbar-right ml-md-auto
6140 if (this.align === 'right') {
6141 cfg.cls += ' navbar-right ml-md-auto';
6143 cfg.cls += ' navbar-left';
6147 if (this.align === 'right') {
6148 cfg.cls += ' navbar-right ml-md-auto';
6150 cfg.cls += ' mr-auto';
6154 cfg.cls += ' navbar-inverse';
6162 * sets the active Navigation item
6163 * @param {Roo.bootstrap.NavItem} the new current navitem
6165 setActiveItem : function(item)
6168 Roo.each(this.navItems, function(v){
6173 v.setActive(false, true);
6180 item.setActive(true, true);
6181 this.fireEvent('changed', this, item, prev);
6186 * gets the active Navigation item
6187 * @return {Roo.bootstrap.NavItem} the current navitem
6189 getActive : function()
6193 Roo.each(this.navItems, function(v){
6204 indexOfNav : function()
6208 Roo.each(this.navItems, function(v,i){
6219 * adds a Navigation item
6220 * @param {Roo.bootstrap.NavItem} the navitem to add
6222 addItem : function(cfg)
6224 if (this.form && Roo.bootstrap.version == 4) {
6227 var cn = new Roo.bootstrap.NavItem(cfg);
6229 cn.parentId = this.id;
6230 cn.onRender(this.el, null);
6234 * register a Navigation item
6235 * @param {Roo.bootstrap.NavItem} the navitem to add
6237 register : function(item)
6239 this.navItems.push( item);
6240 item.navId = this.navId;
6245 * clear all the Navigation item
6248 clearAll : function()
6251 this.el.dom.innerHTML = '';
6254 getNavItem: function(tabId)
6257 Roo.each(this.navItems, function(e) {
6258 if (e.tabId == tabId) {
6268 setActiveNext : function()
6270 var i = this.indexOfNav(this.getActive());
6271 if (i > this.navItems.length) {
6274 this.setActiveItem(this.navItems[i+1]);
6276 setActivePrev : function()
6278 var i = this.indexOfNav(this.getActive());
6282 this.setActiveItem(this.navItems[i-1]);
6284 clearWasActive : function(except) {
6285 Roo.each(this.navItems, function(e) {
6286 if (e.tabId != except.tabId && e.was_active) {
6287 e.was_active = false;
6294 getWasActive : function ()
6297 Roo.each(this.navItems, function(e) {
6312 Roo.apply(Roo.bootstrap.NavGroup, {
6316 * register a Navigation Group
6317 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6319 register : function(navgrp)
6321 this.groups[navgrp.navId] = navgrp;
6325 * fetch a Navigation Group based on the navigation ID
6326 * @param {string} the navgroup to add
6327 * @returns {Roo.bootstrap.NavGroup} the navgroup
6329 get: function(navId) {
6330 if (typeof(this.groups[navId]) == 'undefined') {
6332 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6334 return this.groups[navId] ;
6349 * @class Roo.bootstrap.NavItem
6350 * @extends Roo.bootstrap.Component
6351 * Bootstrap Navbar.NavItem class
6352 * @cfg {String} href link to
6353 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6354 * @cfg {Boolean} button_outline show and outlined button
6355 * @cfg {String} html content of button
6356 * @cfg {String} badge text inside badge
6357 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6358 * @cfg {String} glyphicon DEPRICATED - use fa
6359 * @cfg {String} icon DEPRICATED - use fa
6360 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6361 * @cfg {Boolean} active Is item active
6362 * @cfg {Boolean} disabled Is item disabled
6363 * @cfg {String} linkcls Link Class
6364 * @cfg {Boolean} preventDefault (true | false) default false
6365 * @cfg {String} tabId the tab that this item activates.
6366 * @cfg {String} tagtype (a|span) render as a href or span?
6367 * @cfg {Boolean} animateRef (true|false) link to element default false
6370 * Create a new Navbar Item
6371 * @param {Object} config The config object
6373 Roo.bootstrap.NavItem = function(config){
6374 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6379 * The raw click event for the entire grid.
6380 * @param {Roo.EventObject} e
6385 * Fires when the active item active state changes
6386 * @param {Roo.bootstrap.NavItem} this
6387 * @param {boolean} state the new state
6393 * Fires when scroll to element
6394 * @param {Roo.bootstrap.NavItem} this
6395 * @param {Object} options
6396 * @param {Roo.EventObject} e
6404 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6413 preventDefault : false,
6421 button_outline : false,
6425 getAutoCreate : function(){
6432 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6435 cfg.cls += ' active' ;
6437 if (this.disabled) {
6438 cfg.cls += ' disabled';
6442 if (this.button_weight.length) {
6443 cfg.tag = this.href ? 'a' : 'button';
6444 cfg.html = this.html || '';
6445 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6447 cfg.href = this.href;
6450 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6452 cfg.cls += " nav-html";
6455 // menu .. should add dropdown-menu class - so no need for carat..
6457 if (this.badge !== '') {
6459 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6464 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6468 href : this.href || "#",
6469 html: this.html || '',
6473 if (this.tagtype == 'a') {
6474 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6478 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6479 } else if (this.fa) {
6480 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6481 } else if(this.glyphicon) {
6482 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6484 cfg.cn[0].cls += " nav-html";
6488 cfg.cn[0].html += " <span class='caret'></span>";
6492 if (this.badge !== '') {
6493 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6501 onRender : function(ct, position)
6503 // Roo.log("Call onRender: " + this.xtype);
6504 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6508 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6509 this.navLink = this.el.select('.nav-link',true).first();
6510 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6515 initEvents: function()
6517 if (typeof (this.menu) != 'undefined') {
6518 this.menu.parentType = this.xtype;
6519 this.menu.triggerEl = this.el;
6520 this.menu = this.addxtype(Roo.apply({}, this.menu));
6523 this.el.on('click', this.onClick, this);
6525 //if(this.tagtype == 'span'){
6526 // this.el.select('span',true).on('click', this.onClick, this);
6529 // at this point parent should be available..
6530 this.parent().register(this);
6533 onClick : function(e)
6535 if (e.getTarget('.dropdown-menu-item')) {
6536 // did you click on a menu itemm.... - then don't trigger onclick..
6541 this.preventDefault ||
6544 Roo.log("NavItem - prevent Default?");
6548 if (this.disabled) {
6552 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6553 if (tg && tg.transition) {
6554 Roo.log("waiting for the transitionend");
6560 //Roo.log("fire event clicked");
6561 if(this.fireEvent('click', this, e) === false){
6565 if(this.tagtype == 'span'){
6569 //Roo.log(this.href);
6570 var ael = this.el.select('a',true).first();
6573 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6574 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6575 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6576 return; // ignore... - it's a 'hash' to another page.
6578 Roo.log("NavItem - prevent Default?");
6580 this.scrollToElement(e);
6584 var p = this.parent();
6586 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6587 if (typeof(p.setActiveItem) !== 'undefined') {
6588 p.setActiveItem(this);
6592 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6593 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6594 // remove the collapsed menu expand...
6595 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6599 isActive: function () {
6602 setActive : function(state, fire, is_was_active)
6604 if (this.active && !state && this.navId) {
6605 this.was_active = true;
6606 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6608 nv.clearWasActive(this);
6612 this.active = state;
6615 this.el.removeClass('active');
6616 this.navLink ? this.navLink.removeClass('active') : false;
6617 } else if (!this.el.hasClass('active')) {
6619 this.el.addClass('active');
6620 if (Roo.bootstrap.version == 4 && this.navLink ) {
6621 this.navLink.addClass('active');
6626 this.fireEvent('changed', this, state);
6629 // show a panel if it's registered and related..
6631 if (!this.navId || !this.tabId || !state || is_was_active) {
6635 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6639 var pan = tg.getPanelByName(this.tabId);
6643 // if we can not flip to new panel - go back to old nav highlight..
6644 if (false == tg.showPanel(pan)) {
6645 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6647 var onav = nv.getWasActive();
6649 onav.setActive(true, false, true);
6658 // this should not be here...
6659 setDisabled : function(state)
6661 this.disabled = state;
6663 this.el.removeClass('disabled');
6664 } else if (!this.el.hasClass('disabled')) {
6665 this.el.addClass('disabled');
6671 * Fetch the element to display the tooltip on.
6672 * @return {Roo.Element} defaults to this.el
6674 tooltipEl : function()
6676 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6679 scrollToElement : function(e)
6681 var c = document.body;
6684 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6686 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6687 c = document.documentElement;
6690 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6696 var o = target.calcOffsetsTo(c);
6703 this.fireEvent('scrollto', this, options, e);
6705 Roo.get(c).scrollTo('top', options.value, true);
6710 * Set the HTML (text content) of the item
6711 * @param {string} html content for the nav item
6713 setHtml : function(html)
6716 this.htmlEl.dom.innerHTML = html;
6728 * <span> icon </span>
6729 * <span> text </span>
6730 * <span>badge </span>
6734 * @class Roo.bootstrap.NavSidebarItem
6735 * @extends Roo.bootstrap.NavItem
6736 * Bootstrap Navbar.NavSidebarItem class
6737 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6738 * {Boolean} open is the menu open
6739 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6740 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6741 * {String} buttonSize (sm|md|lg)the extra classes for the button
6742 * {Boolean} showArrow show arrow next to the text (default true)
6744 * Create a new Navbar Button
6745 * @param {Object} config The config object
6747 Roo.bootstrap.NavSidebarItem = function(config){
6748 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6753 * The raw click event for the entire grid.
6754 * @param {Roo.EventObject} e
6759 * Fires when the active item active state changes
6760 * @param {Roo.bootstrap.NavSidebarItem} this
6761 * @param {boolean} state the new state
6769 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6771 badgeWeight : 'default',
6777 buttonWeight : 'default',
6783 getAutoCreate : function(){
6788 href : this.href || '#',
6794 if(this.buttonView){
6797 href : this.href || '#',
6798 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6811 cfg.cls += ' active';
6814 if (this.disabled) {
6815 cfg.cls += ' disabled';
6818 cfg.cls += ' open x-open';
6821 if (this.glyphicon || this.icon) {
6822 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6823 a.cn.push({ tag : 'i', cls : c }) ;
6826 if(!this.buttonView){
6829 html : this.html || ''
6836 if (this.badge !== '') {
6837 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6843 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6846 a.cls += ' dropdown-toggle treeview' ;
6852 initEvents : function()
6854 if (typeof (this.menu) != 'undefined') {
6855 this.menu.parentType = this.xtype;
6856 this.menu.triggerEl = this.el;
6857 this.menu = this.addxtype(Roo.apply({}, this.menu));
6860 this.el.on('click', this.onClick, this);
6862 if(this.badge !== ''){
6863 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6868 onClick : function(e)
6875 if(this.preventDefault){
6879 this.fireEvent('click', this, e);
6882 disable : function()
6884 this.setDisabled(true);
6889 this.setDisabled(false);
6892 setDisabled : function(state)
6894 if(this.disabled == state){
6898 this.disabled = state;
6901 this.el.addClass('disabled');
6905 this.el.removeClass('disabled');
6910 setActive : function(state)
6912 if(this.active == state){
6916 this.active = state;
6919 this.el.addClass('active');
6923 this.el.removeClass('active');
6928 isActive: function ()
6933 setBadge : function(str)
6939 this.badgeEl.dom.innerHTML = str;
6954 Roo.namespace('Roo.bootstrap.breadcrumb');
6958 * @class Roo.bootstrap.breadcrumb.Nav
6959 * @extends Roo.bootstrap.Component
6960 * Bootstrap Breadcrumb Nav Class
6962 * @children Roo.bootstrap.breadcrumb.Item
6965 * Create a new breadcrumb.Nav
6966 * @param {Object} config The config object
6970 Roo.bootstrap.breadcrumb.Nav = function(config){
6971 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6976 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6978 getAutoCreate : function()
6995 initEvents: function()
6997 this.olEl = this.el.select('ol',true).first();
6999 getChildContainer : function()
7015 * @class Roo.bootstrap.breadcrumb.Nav
7016 * @extends Roo.bootstrap.Component
7017 * Bootstrap Breadcrumb Nav Class
7019 * @children Roo.bootstrap.breadcrumb.Component
7020 * @cfg {String} html the content of the link.
7021 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7022 * @cfg {Boolean} active is it active
7026 * Create a new breadcrumb.Nav
7027 * @param {Object} config The config object
7030 Roo.bootstrap.breadcrumb.Item = function(config){
7031 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7036 * The img click event for the img.
7037 * @param {Roo.EventObject} e
7044 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7049 getAutoCreate : function()
7054 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7056 if (this.href !== false) {
7063 cfg.html = this.html;
7069 initEvents: function()
7072 this.el.select('a', true).first().on('click',this.onClick, this)
7076 onClick : function(e)
7079 this.fireEvent('click',this, e);
7092 * @class Roo.bootstrap.Row
7093 * @extends Roo.bootstrap.Component
7094 * Bootstrap Row class (contains columns...)
7098 * @param {Object} config The config object
7101 Roo.bootstrap.Row = function(config){
7102 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7105 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7107 getAutoCreate : function(){
7126 * @class Roo.bootstrap.Pagination
7127 * @extends Roo.bootstrap.Component
7128 * Bootstrap Pagination class
7129 * @cfg {String} size xs | sm | md | lg
7130 * @cfg {Boolean} inverse false | true
7133 * Create a new Pagination
7134 * @param {Object} config The config object
7137 Roo.bootstrap.Pagination = function(config){
7138 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7141 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7147 getAutoCreate : function(){
7153 cfg.cls += ' inverse';
7159 cfg.cls += " " + this.cls;
7177 * @class Roo.bootstrap.PaginationItem
7178 * @extends Roo.bootstrap.Component
7179 * Bootstrap PaginationItem class
7180 * @cfg {String} html text
7181 * @cfg {String} href the link
7182 * @cfg {Boolean} preventDefault (true | false) default true
7183 * @cfg {Boolean} active (true | false) default false
7184 * @cfg {Boolean} disabled default false
7188 * Create a new PaginationItem
7189 * @param {Object} config The config object
7193 Roo.bootstrap.PaginationItem = function(config){
7194 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7199 * The raw click event for the entire grid.
7200 * @param {Roo.EventObject} e
7206 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7210 preventDefault: true,
7215 getAutoCreate : function(){
7221 href : this.href ? this.href : '#',
7222 html : this.html ? this.html : ''
7232 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7236 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7242 initEvents: function() {
7244 this.el.on('click', this.onClick, this);
7247 onClick : function(e)
7249 Roo.log('PaginationItem on click ');
7250 if(this.preventDefault){
7258 this.fireEvent('click', this, e);
7274 * @class Roo.bootstrap.Slider
7275 * @extends Roo.bootstrap.Component
7276 * Bootstrap Slider class
7279 * Create a new Slider
7280 * @param {Object} config The config object
7283 Roo.bootstrap.Slider = function(config){
7284 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7287 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7289 getAutoCreate : function(){
7293 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7297 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7309 * Ext JS Library 1.1.1
7310 * Copyright(c) 2006-2007, Ext JS, LLC.
7312 * Originally Released Under LGPL - original licence link has changed is not relivant.
7315 * <script type="text/javascript">
7318 * @extends Roo.dd.DDProxy
7319 * @class Roo.grid.SplitDragZone
7320 * Support for Column Header resizing
7322 * @param {Object} config
7325 // This is a support class used internally by the Grid components
7326 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7328 this.view = grid.getView();
7329 this.proxy = this.view.resizeProxy;
7330 Roo.grid.SplitDragZone.superclass.constructor.call(
7333 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7335 dragElId : Roo.id(this.proxy.dom),
7340 this.setHandleElId(Roo.id(hd));
7341 if (hd2 !== false) {
7342 this.setOuterHandleElId(Roo.id(hd2));
7345 this.scroll = false;
7347 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7348 fly: Roo.Element.fly,
7350 b4StartDrag : function(x, y){
7351 this.view.headersDisabled = true;
7352 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7353 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7355 this.proxy.setHeight(h);
7357 // for old system colWidth really stored the actual width?
7358 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7359 // which in reality did not work.. - it worked only for fixed sizes
7360 // for resizable we need to use actual sizes.
7361 var w = this.cm.getColumnWidth(this.cellIndex);
7362 if (!this.view.mainWrap) {
7364 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7369 // this was w-this.grid.minColumnWidth;
7370 // doesnt really make sense? - w = thie curren width or the rendered one?
7371 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7372 this.resetConstraints();
7373 this.setXConstraint(minw, 1000);
7374 this.setYConstraint(0, 0);
7375 this.minX = x - minw;
7376 this.maxX = x + 1000;
7378 if (!this.view.mainWrap) { // this is Bootstrap code..
7379 this.getDragEl().style.display='block';
7382 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7386 handleMouseDown : function(e){
7387 ev = Roo.EventObject.setEvent(e);
7388 var t = this.fly(ev.getTarget());
7389 if(t.hasClass("x-grid-split")){
7390 this.cellIndex = this.view.getCellIndex(t.dom);
7392 this.cm = this.grid.colModel;
7393 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7394 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7399 endDrag : function(e){
7400 this.view.headersDisabled = false;
7401 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7402 var diff = endX - this.startPos;
7404 var w = this.cm.getColumnWidth(this.cellIndex);
7405 if (!this.view.mainWrap) {
7408 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7411 autoOffset : function(){
7416 * Ext JS Library 1.1.1
7417 * Copyright(c) 2006-2007, Ext JS, LLC.
7419 * Originally Released Under LGPL - original licence link has changed is not relivant.
7422 * <script type="text/javascript">
7426 * @class Roo.grid.AbstractSelectionModel
7427 * @extends Roo.util.Observable
7429 * Abstract base class for grid SelectionModels. It provides the interface that should be
7430 * implemented by descendant classes. This class should not be directly instantiated.
7433 Roo.grid.AbstractSelectionModel = function(){
7434 this.locked = false;
7435 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7438 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7439 /** @ignore Called by the grid automatically. Do not call directly. */
7440 init : function(grid){
7446 * Locks the selections.
7453 * Unlocks the selections.
7455 unlock : function(){
7456 this.locked = false;
7460 * Returns true if the selections are locked.
7463 isLocked : function(){
7468 * Ext JS Library 1.1.1
7469 * Copyright(c) 2006-2007, Ext JS, LLC.
7471 * Originally Released Under LGPL - original licence link has changed is not relivant.
7474 * <script type="text/javascript">
7477 * @extends Roo.grid.AbstractSelectionModel
7478 * @class Roo.grid.RowSelectionModel
7479 * The default SelectionModel used by {@link Roo.grid.Grid}.
7480 * It supports multiple selections and keyboard selection/navigation.
7482 * @param {Object} config
7484 Roo.grid.RowSelectionModel = function(config){
7485 Roo.apply(this, config);
7486 this.selections = new Roo.util.MixedCollection(false, function(o){
7491 this.lastActive = false;
7495 * @event selectionchange
7496 * Fires when the selection changes
7497 * @param {SelectionModel} this
7499 "selectionchange" : true,
7501 * @event afterselectionchange
7502 * Fires after the selection changes (eg. by key press or clicking)
7503 * @param {SelectionModel} this
7505 "afterselectionchange" : true,
7507 * @event beforerowselect
7508 * Fires when a row is selected being selected, return false to cancel.
7509 * @param {SelectionModel} this
7510 * @param {Number} rowIndex The selected index
7511 * @param {Boolean} keepExisting False if other selections will be cleared
7513 "beforerowselect" : true,
7516 * Fires when a row is selected.
7517 * @param {SelectionModel} this
7518 * @param {Number} rowIndex The selected index
7519 * @param {Roo.data.Record} r The record
7523 * @event rowdeselect
7524 * Fires when a row is deselected.
7525 * @param {SelectionModel} this
7526 * @param {Number} rowIndex The selected index
7528 "rowdeselect" : true
7530 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7531 this.locked = false;
7534 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7536 * @cfg {Boolean} singleSelect
7537 * True to allow selection of only one row at a time (defaults to false)
7539 singleSelect : false,
7542 initEvents : function(){
7544 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7545 this.grid.on("mousedown", this.handleMouseDown, this);
7546 }else{ // allow click to work like normal
7547 this.grid.on("rowclick", this.handleDragableRowClick, this);
7549 // bootstrap does not have a view..
7550 var view = this.grid.view ? this.grid.view : this.grid;
7551 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7554 this.selectPrevious(e.shiftKey);
7555 }else if(this.last !== false && this.lastActive !== false){
7556 var last = this.last;
7557 this.selectRange(this.last, this.lastActive-1);
7558 view.focusRow(this.lastActive);
7563 this.selectFirstRow();
7565 this.fireEvent("afterselectionchange", this);
7567 "down" : function(e){
7569 this.selectNext(e.shiftKey);
7570 }else if(this.last !== false && this.lastActive !== false){
7571 var last = this.last;
7572 this.selectRange(this.last, this.lastActive+1);
7573 view.focusRow(this.lastActive);
7578 this.selectFirstRow();
7580 this.fireEvent("afterselectionchange", this);
7586 view.on("refresh", this.onRefresh, this);
7587 view.on("rowupdated", this.onRowUpdated, this);
7588 view.on("rowremoved", this.onRemove, this);
7592 onRefresh : function(){
7593 var ds = this.grid.ds, i, v = this.grid.view;
7594 var s = this.selections;
7596 if((i = ds.indexOfId(r.id)) != -1){
7598 s.add(ds.getAt(i)); // updating the selection relate data
7606 onRemove : function(v, index, r){
7607 this.selections.remove(r);
7611 onRowUpdated : function(v, index, r){
7612 if(this.isSelected(r)){
7613 v.onRowSelect(index);
7619 * @param {Array} records The records to select
7620 * @param {Boolean} keepExisting (optional) True to keep existing selections
7622 selectRecords : function(records, keepExisting){
7624 this.clearSelections();
7626 var ds = this.grid.ds;
7627 for(var i = 0, len = records.length; i < len; i++){
7628 this.selectRow(ds.indexOf(records[i]), true);
7633 * Gets the number of selected rows.
7636 getCount : function(){
7637 return this.selections.length;
7641 * Selects the first row in the grid.
7643 selectFirstRow : function(){
7648 * Select the last row.
7649 * @param {Boolean} keepExisting (optional) True to keep existing selections
7651 selectLastRow : function(keepExisting){
7652 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7656 * Selects the row immediately following the last selected row.
7657 * @param {Boolean} keepExisting (optional) True to keep existing selections
7659 selectNext : function(keepExisting){
7660 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7661 this.selectRow(this.last+1, keepExisting);
7662 var view = this.grid.view ? this.grid.view : this.grid;
7663 view.focusRow(this.last);
7668 * Selects the row that precedes the last selected row.
7669 * @param {Boolean} keepExisting (optional) True to keep existing selections
7671 selectPrevious : function(keepExisting){
7673 this.selectRow(this.last-1, keepExisting);
7674 var view = this.grid.view ? this.grid.view : this.grid;
7675 view.focusRow(this.last);
7680 * Returns the selected records
7681 * @return {Array} Array of selected records
7683 getSelections : function(){
7684 return [].concat(this.selections.items);
7688 * Returns the first selected record.
7691 getSelected : function(){
7692 return this.selections.itemAt(0);
7697 * Clears all selections.
7699 clearSelections : function(fast){
7704 var ds = this.grid.ds;
7705 var s = this.selections;
7707 this.deselectRow(ds.indexOfId(r.id));
7711 this.selections.clear();
7720 selectAll : function(){
7724 this.selections.clear();
7725 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7726 this.selectRow(i, true);
7731 * Returns True if there is a selection.
7734 hasSelection : function(){
7735 return this.selections.length > 0;
7739 * Returns True if the specified row is selected.
7740 * @param {Number/Record} record The record or index of the record to check
7743 isSelected : function(index){
7744 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7745 return (r && this.selections.key(r.id) ? true : false);
7749 * Returns True if the specified record id is selected.
7750 * @param {String} id The id of record to check
7753 isIdSelected : function(id){
7754 return (this.selections.key(id) ? true : false);
7758 handleMouseDown : function(e, t)
7760 var view = this.grid.view ? this.grid.view : this.grid;
7762 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7765 if(e.shiftKey && this.last !== false){
7766 var last = this.last;
7767 this.selectRange(last, rowIndex, e.ctrlKey);
7768 this.last = last; // reset the last
7769 view.focusRow(rowIndex);
7771 var isSelected = this.isSelected(rowIndex);
7772 if(e.button !== 0 && isSelected){
7773 view.focusRow(rowIndex);
7774 }else if(e.ctrlKey && isSelected){
7775 this.deselectRow(rowIndex);
7776 }else if(!isSelected){
7777 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7778 view.focusRow(rowIndex);
7781 this.fireEvent("afterselectionchange", this);
7784 handleDragableRowClick : function(grid, rowIndex, e)
7786 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7787 this.selectRow(rowIndex, false);
7788 var view = this.grid.view ? this.grid.view : this.grid;
7789 view.focusRow(rowIndex);
7790 this.fireEvent("afterselectionchange", this);
7795 * Selects multiple rows.
7796 * @param {Array} rows Array of the indexes of the row to select
7797 * @param {Boolean} keepExisting (optional) True to keep existing selections
7799 selectRows : function(rows, keepExisting){
7801 this.clearSelections();
7803 for(var i = 0, len = rows.length; i < len; i++){
7804 this.selectRow(rows[i], true);
7809 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7810 * @param {Number} startRow The index of the first row in the range
7811 * @param {Number} endRow The index of the last row in the range
7812 * @param {Boolean} keepExisting (optional) True to retain existing selections
7814 selectRange : function(startRow, endRow, keepExisting){
7819 this.clearSelections();
7821 if(startRow <= endRow){
7822 for(var i = startRow; i <= endRow; i++){
7823 this.selectRow(i, true);
7826 for(var i = startRow; i >= endRow; i--){
7827 this.selectRow(i, true);
7833 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7834 * @param {Number} startRow The index of the first row in the range
7835 * @param {Number} endRow The index of the last row in the range
7837 deselectRange : function(startRow, endRow, preventViewNotify){
7841 for(var i = startRow; i <= endRow; i++){
7842 this.deselectRow(i, preventViewNotify);
7848 * @param {Number} row The index of the row to select
7849 * @param {Boolean} keepExisting (optional) True to keep existing selections
7851 selectRow : function(index, keepExisting, preventViewNotify){
7852 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7855 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7856 if(!keepExisting || this.singleSelect){
7857 this.clearSelections();
7859 var r = this.grid.ds.getAt(index);
7860 this.selections.add(r);
7861 this.last = this.lastActive = index;
7862 if(!preventViewNotify){
7863 var view = this.grid.view ? this.grid.view : this.grid;
7864 view.onRowSelect(index);
7866 this.fireEvent("rowselect", this, index, r);
7867 this.fireEvent("selectionchange", this);
7873 * @param {Number} row The index of the row to deselect
7875 deselectRow : function(index, preventViewNotify){
7879 if(this.last == index){
7882 if(this.lastActive == index){
7883 this.lastActive = false;
7885 var r = this.grid.ds.getAt(index);
7886 this.selections.remove(r);
7887 if(!preventViewNotify){
7888 var view = this.grid.view ? this.grid.view : this.grid;
7889 view.onRowDeselect(index);
7891 this.fireEvent("rowdeselect", this, index);
7892 this.fireEvent("selectionchange", this);
7896 restoreLast : function(){
7898 this.last = this._last;
7903 acceptsNav : function(row, col, cm){
7904 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7908 onEditorKey : function(field, e){
7909 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7914 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7916 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7918 }else if(k == e.ENTER && !e.ctrlKey){
7922 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7924 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7926 }else if(k == e.ESC){
7930 g.startEditing(newCell[0], newCell[1]);
7935 * Ext JS Library 1.1.1
7936 * Copyright(c) 2006-2007, Ext JS, LLC.
7938 * Originally Released Under LGPL - original licence link has changed is not relivant.
7941 * <script type="text/javascript">
7946 * @class Roo.grid.ColumnModel
7947 * @extends Roo.util.Observable
7948 * This is the default implementation of a ColumnModel used by the Grid. It defines
7949 * the columns in the grid.
7952 var colModel = new Roo.grid.ColumnModel([
7953 {header: "Ticker", width: 60, sortable: true, locked: true},
7954 {header: "Company Name", width: 150, sortable: true},
7955 {header: "Market Cap.", width: 100, sortable: true},
7956 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7957 {header: "Employees", width: 100, sortable: true, resizable: false}
7962 * The config options listed for this class are options which may appear in each
7963 * individual column definition.
7964 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7966 * @param {Object} config An Array of column config objects. See this class's
7967 * config objects for details.
7969 Roo.grid.ColumnModel = function(config){
7971 * The config passed into the constructor
7973 this.config = []; //config;
7976 // if no id, create one
7977 // if the column does not have a dataIndex mapping,
7978 // map it to the order it is in the config
7979 for(var i = 0, len = config.length; i < len; i++){
7980 this.addColumn(config[i]);
7985 * The width of columns which have no width specified (defaults to 100)
7988 this.defaultWidth = 100;
7991 * Default sortable of columns which have no sortable specified (defaults to false)
7994 this.defaultSortable = false;
7998 * @event widthchange
7999 * Fires when the width of a column changes.
8000 * @param {ColumnModel} this
8001 * @param {Number} columnIndex The column index
8002 * @param {Number} newWidth The new width
8004 "widthchange": true,
8006 * @event headerchange
8007 * Fires when the text of a header changes.
8008 * @param {ColumnModel} this
8009 * @param {Number} columnIndex The column index
8010 * @param {Number} newText The new header text
8012 "headerchange": true,
8014 * @event hiddenchange
8015 * Fires when a column is hidden or "unhidden".
8016 * @param {ColumnModel} this
8017 * @param {Number} columnIndex The column index
8018 * @param {Boolean} hidden true if hidden, false otherwise
8020 "hiddenchange": true,
8022 * @event columnmoved
8023 * Fires when a column is moved.
8024 * @param {ColumnModel} this
8025 * @param {Number} oldIndex
8026 * @param {Number} newIndex
8028 "columnmoved" : true,
8030 * @event columlockchange
8031 * Fires when a column's locked state is changed
8032 * @param {ColumnModel} this
8033 * @param {Number} colIndex
8034 * @param {Boolean} locked true if locked
8036 "columnlockchange" : true
8038 Roo.grid.ColumnModel.superclass.constructor.call(this);
8040 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8042 * @cfg {String} header The header text to display in the Grid view.
8045 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8048 * @cfg {String} smHeader Header at Bootsrap Small width
8051 * @cfg {String} mdHeader Header at Bootsrap Medium width
8054 * @cfg {String} lgHeader Header at Bootsrap Large width
8057 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8060 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8061 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8062 * specified, the column's index is used as an index into the Record's data Array.
8065 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8066 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8069 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8070 * Defaults to the value of the {@link #defaultSortable} property.
8071 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8074 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8077 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8080 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8083 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8086 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8087 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8088 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8089 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8092 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8095 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8098 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8101 * @cfg {String} cursor (Optional)
8104 * @cfg {String} tooltip (Optional)
8107 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8110 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8113 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8116 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8119 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8122 * Returns the id of the column at the specified index.
8123 * @param {Number} index The column index
8124 * @return {String} the id
8126 getColumnId : function(index){
8127 return this.config[index].id;
8131 * Returns the column for a specified id.
8132 * @param {String} id The column id
8133 * @return {Object} the column
8135 getColumnById : function(id){
8136 return this.lookup[id];
8141 * Returns the column Object for a specified dataIndex.
8142 * @param {String} dataIndex The column dataIndex
8143 * @return {Object|Boolean} the column or false if not found
8145 getColumnByDataIndex: function(dataIndex){
8146 var index = this.findColumnIndex(dataIndex);
8147 return index > -1 ? this.config[index] : false;
8151 * Returns the index for a specified column id.
8152 * @param {String} id The column id
8153 * @return {Number} the index, or -1 if not found
8155 getIndexById : function(id){
8156 for(var i = 0, len = this.config.length; i < len; i++){
8157 if(this.config[i].id == id){
8165 * Returns the index for a specified column dataIndex.
8166 * @param {String} dataIndex The column dataIndex
8167 * @return {Number} the index, or -1 if not found
8170 findColumnIndex : function(dataIndex){
8171 for(var i = 0, len = this.config.length; i < len; i++){
8172 if(this.config[i].dataIndex == dataIndex){
8180 moveColumn : function(oldIndex, newIndex){
8181 var c = this.config[oldIndex];
8182 this.config.splice(oldIndex, 1);
8183 this.config.splice(newIndex, 0, c);
8184 this.dataMap = null;
8185 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8188 isLocked : function(colIndex){
8189 return this.config[colIndex].locked === true;
8192 setLocked : function(colIndex, value, suppressEvent){
8193 if(this.isLocked(colIndex) == value){
8196 this.config[colIndex].locked = value;
8198 this.fireEvent("columnlockchange", this, colIndex, value);
8202 getTotalLockedWidth : function(){
8204 for(var i = 0; i < this.config.length; i++){
8205 if(this.isLocked(i) && !this.isHidden(i)){
8206 this.totalWidth += this.getColumnWidth(i);
8212 getLockedCount : function(){
8213 for(var i = 0, len = this.config.length; i < len; i++){
8214 if(!this.isLocked(i)){
8219 return this.config.length;
8223 * Returns the number of columns.
8226 getColumnCount : function(visibleOnly){
8227 if(visibleOnly === true){
8229 for(var i = 0, len = this.config.length; i < len; i++){
8230 if(!this.isHidden(i)){
8236 return this.config.length;
8240 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8241 * @param {Function} fn
8242 * @param {Object} scope (optional)
8243 * @return {Array} result
8245 getColumnsBy : function(fn, scope){
8247 for(var i = 0, len = this.config.length; i < len; i++){
8248 var c = this.config[i];
8249 if(fn.call(scope||this, c, i) === true){
8257 * Returns true if the specified column is sortable.
8258 * @param {Number} col The column index
8261 isSortable : function(col){
8262 if(typeof this.config[col].sortable == "undefined"){
8263 return this.defaultSortable;
8265 return this.config[col].sortable;
8269 * Returns the rendering (formatting) function defined for the column.
8270 * @param {Number} col The column index.
8271 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8273 getRenderer : function(col){
8274 if(!this.config[col].renderer){
8275 return Roo.grid.ColumnModel.defaultRenderer;
8277 return this.config[col].renderer;
8281 * Sets the rendering (formatting) function for a column.
8282 * @param {Number} col The column index
8283 * @param {Function} fn The function to use to process the cell's raw data
8284 * to return HTML markup for the grid view. The render function is called with
8285 * the following parameters:<ul>
8286 * <li>Data value.</li>
8287 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8288 * <li>css A CSS style string to apply to the table cell.</li>
8289 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8290 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8291 * <li>Row index</li>
8292 * <li>Column index</li>
8293 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8295 setRenderer : function(col, fn){
8296 this.config[col].renderer = fn;
8300 * Returns the width for the specified column.
8301 * @param {Number} col The column index
8302 * @param (optional) {String} gridSize bootstrap width size.
8305 getColumnWidth : function(col, gridSize)
8307 var cfg = this.config[col];
8309 if (typeof(gridSize) == 'undefined') {
8310 return cfg.width * 1 || this.defaultWidth;
8312 if (gridSize === false) { // if we set it..
8313 return cfg.width || false;
8315 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8317 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8318 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8321 return cfg[ sizes[i] ];
8328 * Sets the width for a column.
8329 * @param {Number} col The column index
8330 * @param {Number} width The new width
8332 setColumnWidth : function(col, width, suppressEvent){
8333 this.config[col].width = width;
8334 this.totalWidth = null;
8336 this.fireEvent("widthchange", this, col, width);
8341 * Returns the total width of all columns.
8342 * @param {Boolean} includeHidden True to include hidden column widths
8345 getTotalWidth : function(includeHidden){
8346 if(!this.totalWidth){
8347 this.totalWidth = 0;
8348 for(var i = 0, len = this.config.length; i < len; i++){
8349 if(includeHidden || !this.isHidden(i)){
8350 this.totalWidth += this.getColumnWidth(i);
8354 return this.totalWidth;
8358 * Returns the header for the specified column.
8359 * @param {Number} col The column index
8362 getColumnHeader : function(col){
8363 return this.config[col].header;
8367 * Sets the header for a column.
8368 * @param {Number} col The column index
8369 * @param {String} header The new header
8371 setColumnHeader : function(col, header){
8372 this.config[col].header = header;
8373 this.fireEvent("headerchange", this, col, header);
8377 * Returns the tooltip for the specified column.
8378 * @param {Number} col The column index
8381 getColumnTooltip : function(col){
8382 return this.config[col].tooltip;
8385 * Sets the tooltip for a column.
8386 * @param {Number} col The column index
8387 * @param {String} tooltip The new tooltip
8389 setColumnTooltip : function(col, tooltip){
8390 this.config[col].tooltip = tooltip;
8394 * Returns the dataIndex for the specified column.
8395 * @param {Number} col The column index
8398 getDataIndex : function(col){
8399 return this.config[col].dataIndex;
8403 * Sets the dataIndex for a column.
8404 * @param {Number} col The column index
8405 * @param {Number} dataIndex The new dataIndex
8407 setDataIndex : function(col, dataIndex){
8408 this.config[col].dataIndex = dataIndex;
8414 * Returns true if the cell is editable.
8415 * @param {Number} colIndex The column index
8416 * @param {Number} rowIndex The row index - this is nto actually used..?
8419 isCellEditable : function(colIndex, rowIndex){
8420 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8424 * Returns the editor defined for the cell/column.
8425 * return false or null to disable editing.
8426 * @param {Number} colIndex The column index
8427 * @param {Number} rowIndex The row index
8430 getCellEditor : function(colIndex, rowIndex){
8431 return this.config[colIndex].editor;
8435 * Sets if a column is editable.
8436 * @param {Number} col The column index
8437 * @param {Boolean} editable True if the column is editable
8439 setEditable : function(col, editable){
8440 this.config[col].editable = editable;
8445 * Returns true if the column is hidden.
8446 * @param {Number} colIndex The column index
8449 isHidden : function(colIndex){
8450 return this.config[colIndex].hidden;
8455 * Returns true if the column width cannot be changed
8457 isFixed : function(colIndex){
8458 return this.config[colIndex].fixed;
8462 * Returns true if the column can be resized
8465 isResizable : function(colIndex){
8466 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8469 * Sets if a column is hidden.
8470 * @param {Number} colIndex The column index
8471 * @param {Boolean} hidden True if the column is hidden
8473 setHidden : function(colIndex, hidden){
8474 this.config[colIndex].hidden = hidden;
8475 this.totalWidth = null;
8476 this.fireEvent("hiddenchange", this, colIndex, hidden);
8480 * Sets the editor for a column.
8481 * @param {Number} col The column index
8482 * @param {Object} editor The editor object
8484 setEditor : function(col, editor){
8485 this.config[col].editor = editor;
8488 * Add a column (experimental...) - defaults to adding to the end..
8489 * @param {Object} config
8491 addColumn : function(c)
8494 var i = this.config.length;
8497 if(typeof c.dataIndex == "undefined"){
8500 if(typeof c.renderer == "string"){
8501 c.renderer = Roo.util.Format[c.renderer];
8503 if(typeof c.id == "undefined"){
8506 if(c.editor && c.editor.xtype){
8507 c.editor = Roo.factory(c.editor, Roo.grid);
8509 if(c.editor && c.editor.isFormField){
8510 c.editor = new Roo.grid.GridEditor(c.editor);
8512 this.lookup[c.id] = c;
8517 Roo.grid.ColumnModel.defaultRenderer = function(value)
8519 if(typeof value == "object") {
8522 if(typeof value == "string" && value.length < 1){
8526 return String.format("{0}", value);
8529 // Alias for backwards compatibility
8530 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8533 * Ext JS Library 1.1.1
8534 * Copyright(c) 2006-2007, Ext JS, LLC.
8536 * Originally Released Under LGPL - original licence link has changed is not relivant.
8539 * <script type="text/javascript">
8543 * @class Roo.LoadMask
8544 * A simple utility class for generically masking elements while loading data. If the element being masked has
8545 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8546 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8547 * element's UpdateManager load indicator and will be destroyed after the initial load.
8549 * Create a new LoadMask
8550 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8551 * @param {Object} config The config object
8553 Roo.LoadMask = function(el, config){
8554 this.el = Roo.get(el);
8555 Roo.apply(this, config);
8557 this.store.on('beforeload', this.onBeforeLoad, this);
8558 this.store.on('load', this.onLoad, this);
8559 this.store.on('loadexception', this.onLoadException, this);
8560 this.removeMask = false;
8562 var um = this.el.getUpdateManager();
8563 um.showLoadIndicator = false; // disable the default indicator
8564 um.on('beforeupdate', this.onBeforeLoad, this);
8565 um.on('update', this.onLoad, this);
8566 um.on('failure', this.onLoad, this);
8567 this.removeMask = true;
8571 Roo.LoadMask.prototype = {
8573 * @cfg {Boolean} removeMask
8574 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8575 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8580 * The text to display in a centered loading message box (defaults to 'Loading...')
8584 * @cfg {String} msgCls
8585 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8587 msgCls : 'x-mask-loading',
8590 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8596 * Disables the mask to prevent it from being displayed
8598 disable : function(){
8599 this.disabled = true;
8603 * Enables the mask so that it can be displayed
8605 enable : function(){
8606 this.disabled = false;
8609 onLoadException : function()
8613 if (typeof(arguments[3]) != 'undefined') {
8614 Roo.MessageBox.alert("Error loading",arguments[3]);
8618 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8619 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8626 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8631 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8635 onBeforeLoad : function(){
8637 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8642 destroy : function(){
8644 this.store.un('beforeload', this.onBeforeLoad, this);
8645 this.store.un('load', this.onLoad, this);
8646 this.store.un('loadexception', this.onLoadException, this);
8648 var um = this.el.getUpdateManager();
8649 um.un('beforeupdate', this.onBeforeLoad, this);
8650 um.un('update', this.onLoad, this);
8651 um.un('failure', this.onLoad, this);
8655 * @class Roo.bootstrap.Table
8657 * @extends Roo.bootstrap.Component
8658 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8659 * Similar to Roo.grid.Grid
8661 var table = Roo.factory({
8663 xns : Roo.bootstrap,
8664 autoSizeColumns: true,
8671 sortInfo : { direction : 'ASC', field: 'name' },
8673 xtype : 'HttpProxy',
8676 url : 'https://example.com/some.data.url.json'
8679 xtype : 'JsonReader',
8681 fields : [ 'id', 'name', whatever' ],
8688 xtype : 'ColumnModel',
8692 dataIndex : 'is_in_group',
8695 renderer : function(v, x , r) {
8697 return String.format("{0}", v)
8703 xtype : 'RowSelectionModel',
8704 xns : Roo.bootstrap.Table
8705 // you can add listeners to catch selection change here....
8711 grid.render(Roo.get("some-div"));
8714 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8719 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8720 * @cfg {Roo.data.Store} store The data store to use
8721 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8723 * @cfg {String} cls table class
8726 * @cfg {boolean} striped Should the rows be alternative striped
8727 * @cfg {boolean} bordered Add borders to the table
8728 * @cfg {boolean} hover Add hover highlighting
8729 * @cfg {boolean} condensed Format condensed
8730 * @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,
8731 * also adds table-responsive (see bootstrap docs for details)
8732 * @cfg {Boolean} loadMask (true|false) default false
8733 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8734 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8735 * @cfg {Boolean} rowSelection (true|false) default false
8736 * @cfg {Boolean} cellSelection (true|false) default false
8737 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8738 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8739 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8740 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8741 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8742 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8745 * Create a new Table
8746 * @param {Object} config The config object
8749 Roo.bootstrap.Table = function(config)
8751 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8754 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8755 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8756 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8757 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8759 this.view = this; // compat with grid.
8761 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8763 this.sm.grid = this;
8764 this.selModel = Roo.factory(this.sm, Roo.grid);
8765 this.sm = this.selModel;
8766 this.sm.xmodule = this.xmodule || false;
8769 if (this.cm && typeof(this.cm.config) == 'undefined') {
8770 this.colModel = new Roo.grid.ColumnModel(this.cm);
8771 this.cm = this.colModel;
8772 this.cm.xmodule = this.xmodule || false;
8775 this.store= Roo.factory(this.store, Roo.data);
8776 this.ds = this.store;
8777 this.ds.xmodule = this.xmodule || false;
8780 if (this.footer && this.store) {
8781 this.footer.dataSource = this.ds;
8782 this.footer = Roo.factory(this.footer);
8789 * Fires when a cell is clicked
8790 * @param {Roo.bootstrap.Table} this
8791 * @param {Roo.Element} el
8792 * @param {Number} rowIndex
8793 * @param {Number} columnIndex
8794 * @param {Roo.EventObject} e
8798 * @event celldblclick
8799 * Fires when a cell is double clicked
8800 * @param {Roo.bootstrap.Table} this
8801 * @param {Roo.Element} el
8802 * @param {Number} rowIndex
8803 * @param {Number} columnIndex
8804 * @param {Roo.EventObject} e
8806 "celldblclick" : true,
8809 * Fires when a row is clicked
8810 * @param {Roo.bootstrap.Table} this
8811 * @param {Roo.Element} el
8812 * @param {Number} rowIndex
8813 * @param {Roo.EventObject} e
8817 * @event rowdblclick
8818 * Fires when a row is double clicked
8819 * @param {Roo.bootstrap.Table} this
8820 * @param {Roo.Element} el
8821 * @param {Number} rowIndex
8822 * @param {Roo.EventObject} e
8824 "rowdblclick" : true,
8827 * Fires when a mouseover occur
8828 * @param {Roo.bootstrap.Table} this
8829 * @param {Roo.Element} el
8830 * @param {Number} rowIndex
8831 * @param {Number} columnIndex
8832 * @param {Roo.EventObject} e
8837 * Fires when a mouseout occur
8838 * @param {Roo.bootstrap.Table} this
8839 * @param {Roo.Element} el
8840 * @param {Number} rowIndex
8841 * @param {Number} columnIndex
8842 * @param {Roo.EventObject} e
8847 * Fires when a row is rendered, so you can change add a style to it.
8848 * @param {Roo.bootstrap.Table} this
8849 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8853 * @event rowsrendered
8854 * Fires when all the rows have been rendered
8855 * @param {Roo.bootstrap.Table} this
8857 'rowsrendered' : true,
8859 * @event contextmenu
8860 * The raw contextmenu event for the entire grid.
8861 * @param {Roo.EventObject} e
8863 "contextmenu" : true,
8865 * @event rowcontextmenu
8866 * Fires when a row is right clicked
8867 * @param {Roo.bootstrap.Table} this
8868 * @param {Number} rowIndex
8869 * @param {Roo.EventObject} e
8871 "rowcontextmenu" : true,
8873 * @event cellcontextmenu
8874 * Fires when a cell is right clicked
8875 * @param {Roo.bootstrap.Table} this
8876 * @param {Number} rowIndex
8877 * @param {Number} cellIndex
8878 * @param {Roo.EventObject} e
8880 "cellcontextmenu" : true,
8882 * @event headercontextmenu
8883 * Fires when a header is right clicked
8884 * @param {Roo.bootstrap.Table} this
8885 * @param {Number} columnIndex
8886 * @param {Roo.EventObject} e
8888 "headercontextmenu" : true,
8891 * The raw mousedown event for the entire grid.
8892 * @param {Roo.EventObject} e
8899 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8915 enableColumnResize: true,
8917 rowSelection : false,
8918 cellSelection : false,
8921 minColumnWidth : 50,
8923 // Roo.Element - the tbody
8924 bodyEl: false, // <tbody> Roo.Element - thead element
8925 headEl: false, // <thead> Roo.Element - thead element
8926 resizeProxy : false, // proxy element for dragging?
8930 container: false, // used by gridpanel...
8936 auto_hide_footer : false,
8938 view: false, // actually points to this..
8940 getAutoCreate : function()
8942 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8949 // this get's auto added by panel.Grid
8950 if (this.scrollBody) {
8951 cfg.cls += ' table-body-fixed';
8954 cfg.cls += ' table-striped';
8958 cfg.cls += ' table-hover';
8960 if (this.bordered) {
8961 cfg.cls += ' table-bordered';
8963 if (this.condensed) {
8964 cfg.cls += ' table-condensed';
8967 if (this.responsive) {
8968 cfg.cls += ' table-responsive';
8972 cfg.cls+= ' ' +this.cls;
8978 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8981 if(this.store || this.cm){
8982 if(this.headerShow){
8983 cfg.cn.push(this.renderHeader());
8986 cfg.cn.push(this.renderBody());
8988 if(this.footerShow){
8989 cfg.cn.push(this.renderFooter());
8991 // where does this come from?
8992 //cfg.cls+= ' TableGrid';
8995 return { cn : [ cfg ] };
8998 initEvents : function()
9000 if(!this.store || !this.cm){
9003 if (this.selModel) {
9004 this.selModel.initEvents();
9008 //Roo.log('initEvents with ds!!!!');
9010 this.bodyEl = this.el.select('tbody', true).first();
9011 this.headEl = this.el.select('thead', true).first();
9012 this.mainFoot = this.el.select('tfoot', true).first();
9017 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9018 e.on('click', this.sort, this);
9022 // why is this done????? = it breaks dialogs??
9023 //this.parent().el.setStyle('position', 'relative');
9027 this.footer.parentId = this.id;
9028 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9031 this.el.select('tfoot tr td').first().addClass('hide');
9036 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9039 this.store.on('load', this.onLoad, this);
9040 this.store.on('beforeload', this.onBeforeLoad, this);
9041 this.store.on('update', this.onUpdate, this);
9042 this.store.on('add', this.onAdd, this);
9043 this.store.on("clear", this.clear, this);
9045 this.el.on("contextmenu", this.onContextMenu, this);
9048 this.cm.on("headerchange", this.onHeaderChange, this);
9049 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9051 //?? does bodyEl get replaced on render?
9052 this.bodyEl.on("click", this.onClick, this);
9053 this.bodyEl.on("dblclick", this.onDblClick, this);
9054 this.bodyEl.on('scroll', this.onBodyScroll, this);
9056 // guessing mainbody will work - this relays usually caught by selmodel at present.
9057 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9060 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9063 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9064 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9069 // Compatibility with grid - we implement all the view features at present.
9070 getView : function()
9075 initCSS : function()
9079 var cm = this.cm, styles = [];
9080 this.CSS.removeStyleSheet(this.id + '-cssrules');
9081 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9082 // we can honour xs/sm/md/xl as widths...
9083 // we first have to decide what widht we are currently at...
9084 var sz = Roo.getGridSize();
9088 var cols = []; // visable cols.
9090 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9091 var w = cm.getColumnWidth(i, false);
9093 cols.push( { rel : false, abs : 0 });
9097 cols.push( { rel : false, abs : w });
9099 last = i; // not really..
9102 var w = cm.getColumnWidth(i, sz);
9107 cols.push( { rel : w, abs : false });
9110 var avail = this.bodyEl.dom.clientWidth - total_abs;
9112 var unitWidth = Math.floor(avail / total);
9113 var rem = avail - (unitWidth * total);
9115 var hidden, width, pos = 0 , splithide , left;
9116 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9118 hidden = 'display:none;';
9120 width = 'width:0px;';
9122 if(!cm.isHidden(i)){
9126 // we can honour xs/sm/md/xl ?
9127 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9129 hidden = 'display:none;';
9131 // width should return a small number...
9133 w+=rem; // add the remaining with..
9136 left = "left:" + (pos -4) + "px;";
9137 width = "width:" + w+ "px;";
9140 if (this.responsive) {
9143 hidden = cm.isHidden(i) ? 'display:none;' : '';
9144 splithide = 'display: none;';
9147 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9150 splithide = 'display:none;';
9153 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9154 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9159 //Roo.log(styles.join(''));
9160 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9166 onContextMenu : function(e, t)
9168 this.processEvent("contextmenu", e);
9171 processEvent : function(name, e)
9173 if (name != 'touchstart' ) {
9174 this.fireEvent(name, e);
9177 var t = e.getTarget();
9179 var cell = Roo.get(t);
9185 if(cell.findParent('tfoot', false, true)){
9189 if(cell.findParent('thead', false, true)){
9191 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9192 cell = Roo.get(t).findParent('th', false, true);
9194 Roo.log("failed to find th in thead?");
9195 Roo.log(e.getTarget());
9200 var cellIndex = cell.dom.cellIndex;
9202 var ename = name == 'touchstart' ? 'click' : name;
9203 this.fireEvent("header" + ename, this, cellIndex, e);
9208 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9209 cell = Roo.get(t).findParent('td', false, true);
9211 Roo.log("failed to find th in tbody?");
9212 Roo.log(e.getTarget());
9217 var row = cell.findParent('tr', false, true);
9218 var cellIndex = cell.dom.cellIndex;
9219 var rowIndex = row.dom.rowIndex - 1;
9223 this.fireEvent("row" + name, this, rowIndex, e);
9227 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9233 onMouseover : function(e, el)
9235 var cell = Roo.get(el);
9241 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9242 cell = cell.findParent('td', false, true);
9245 var row = cell.findParent('tr', false, true);
9246 var cellIndex = cell.dom.cellIndex;
9247 var rowIndex = row.dom.rowIndex - 1; // start from 0
9249 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9253 onMouseout : function(e, el)
9255 var cell = Roo.get(el);
9261 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9262 cell = cell.findParent('td', false, true);
9265 var row = cell.findParent('tr', false, true);
9266 var cellIndex = cell.dom.cellIndex;
9267 var rowIndex = row.dom.rowIndex - 1; // start from 0
9269 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9273 onClick : function(e, el)
9275 var cell = Roo.get(el);
9277 if(!cell || (!this.cellSelection && !this.rowSelection)){
9281 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9282 cell = cell.findParent('td', false, true);
9285 if(!cell || typeof(cell) == 'undefined'){
9289 var row = cell.findParent('tr', false, true);
9291 if(!row || typeof(row) == 'undefined'){
9295 var cellIndex = cell.dom.cellIndex;
9296 var rowIndex = this.getRowIndex(row);
9298 // why??? - should these not be based on SelectionModel?
9299 //if(this.cellSelection){
9300 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9303 //if(this.rowSelection){
9304 this.fireEvent('rowclick', this, row, rowIndex, e);
9309 onDblClick : function(e,el)
9311 var cell = Roo.get(el);
9313 if(!cell || (!this.cellSelection && !this.rowSelection)){
9317 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9318 cell = cell.findParent('td', false, true);
9321 if(!cell || typeof(cell) == 'undefined'){
9325 var row = cell.findParent('tr', false, true);
9327 if(!row || typeof(row) == 'undefined'){
9331 var cellIndex = cell.dom.cellIndex;
9332 var rowIndex = this.getRowIndex(row);
9334 if(this.cellSelection){
9335 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9338 if(this.rowSelection){
9339 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9342 findRowIndex : function(el)
9344 var cell = Roo.get(el);
9348 var row = cell.findParent('tr', false, true);
9350 if(!row || typeof(row) == 'undefined'){
9353 return this.getRowIndex(row);
9355 sort : function(e,el)
9357 var col = Roo.get(el);
9359 if(!col.hasClass('sortable')){
9363 var sort = col.attr('sort');
9366 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9370 this.store.sortInfo = {field : sort, direction : dir};
9373 Roo.log("calling footer first");
9374 this.footer.onClick('first');
9377 this.store.load({ params : { start : 0 } });
9381 renderHeader : function()
9389 this.totalWidth = 0;
9391 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9393 var config = cm.config[i];
9397 cls : 'x-hcol-' + i,
9400 html: cm.getColumnHeader(i)
9403 var tooltip = cm.getColumnTooltip(i);
9405 c.tooltip = tooltip;
9411 if(typeof(config.sortable) != 'undefined' && config.sortable){
9412 c.cls += ' sortable';
9413 c.html = '<i class="fa"></i>' + c.html;
9416 // could use BS4 hidden-..-down
9418 if(typeof(config.lgHeader) != 'undefined'){
9419 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9422 if(typeof(config.mdHeader) != 'undefined'){
9423 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9426 if(typeof(config.smHeader) != 'undefined'){
9427 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9430 if(typeof(config.xsHeader) != 'undefined'){
9431 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9438 if(typeof(config.tooltip) != 'undefined'){
9439 c.tooltip = config.tooltip;
9442 if(typeof(config.colspan) != 'undefined'){
9443 c.colspan = config.colspan;
9446 // hidden is handled by CSS now
9448 if(typeof(config.dataIndex) != 'undefined'){
9449 c.sort = config.dataIndex;
9454 if(typeof(config.align) != 'undefined' && config.align.length){
9455 c.style += ' text-align:' + config.align + ';';
9458 /* width is done in CSS
9459 *if(typeof(config.width) != 'undefined'){
9460 c.style += ' width:' + config.width + 'px;';
9461 this.totalWidth += config.width;
9463 this.totalWidth += 100; // assume minimum of 100 per column?
9467 if(typeof(config.cls) != 'undefined'){
9468 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9470 // this is the bit that doesnt reall work at all...
9472 if (this.responsive) {
9475 ['xs','sm','md','lg'].map(function(size){
9477 if(typeof(config[size]) == 'undefined'){
9481 if (!config[size]) { // 0 = hidden
9482 // BS 4 '0' is treated as hide that column and below.
9483 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9487 c.cls += ' col-' + size + '-' + config[size] + (
9488 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9496 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9507 renderBody : function()
9517 colspan : this.cm.getColumnCount()
9527 renderFooter : function()
9537 colspan : this.cm.getColumnCount()
9551 // Roo.log('ds onload');
9556 var ds = this.store;
9558 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9559 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9560 if (_this.store.sortInfo) {
9562 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9563 e.select('i', true).addClass(['fa-arrow-up']);
9566 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9567 e.select('i', true).addClass(['fa-arrow-down']);
9572 var tbody = this.bodyEl;
9574 if(ds.getCount() > 0){
9575 ds.data.each(function(d,rowIndex){
9576 var row = this.renderRow(cm, ds, rowIndex);
9578 tbody.createChild(row);
9582 if(row.cellObjects.length){
9583 Roo.each(row.cellObjects, function(r){
9584 _this.renderCellObject(r);
9591 var tfoot = this.el.select('tfoot', true).first();
9593 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9595 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9597 var total = this.ds.getTotalCount();
9599 if(this.footer.pageSize < total){
9600 this.mainFoot.show();
9604 Roo.each(this.el.select('tbody td', true).elements, function(e){
9605 e.on('mouseover', _this.onMouseover, _this);
9608 Roo.each(this.el.select('tbody td', true).elements, function(e){
9609 e.on('mouseout', _this.onMouseout, _this);
9611 this.fireEvent('rowsrendered', this);
9615 this.initCSS(); /// resize cols
9621 onUpdate : function(ds,record)
9623 this.refreshRow(record);
9627 onRemove : function(ds, record, index, isUpdate){
9628 if(isUpdate !== true){
9629 this.fireEvent("beforerowremoved", this, index, record);
9631 var bt = this.bodyEl.dom;
9633 var rows = this.el.select('tbody > tr', true).elements;
9635 if(typeof(rows[index]) != 'undefined'){
9636 bt.removeChild(rows[index].dom);
9639 // if(bt.rows[index]){
9640 // bt.removeChild(bt.rows[index]);
9643 if(isUpdate !== true){
9644 //this.stripeRows(index);
9645 //this.syncRowHeights(index, index);
9647 this.fireEvent("rowremoved", this, index, record);
9651 onAdd : function(ds, records, rowIndex)
9653 //Roo.log('on Add called');
9654 // - note this does not handle multiple adding very well..
9655 var bt = this.bodyEl.dom;
9656 for (var i =0 ; i < records.length;i++) {
9657 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9658 //Roo.log(records[i]);
9659 //Roo.log(this.store.getAt(rowIndex+i));
9660 this.insertRow(this.store, rowIndex + i, false);
9667 refreshRow : function(record){
9668 var ds = this.store, index;
9669 if(typeof record == 'number'){
9671 record = ds.getAt(index);
9673 index = ds.indexOf(record);
9675 return; // should not happen - but seems to
9678 this.insertRow(ds, index, true);
9680 this.onRemove(ds, record, index+1, true);
9682 //this.syncRowHeights(index, index);
9684 this.fireEvent("rowupdated", this, index, record);
9686 // private - called by RowSelection
9687 onRowSelect : function(rowIndex){
9688 var row = this.getRowDom(rowIndex);
9689 row.addClass(['bg-info','info']);
9691 // private - called by RowSelection
9692 onRowDeselect : function(rowIndex)
9697 var row = this.getRowDom(rowIndex);
9698 row.removeClass(['bg-info','info']);
9701 * Focuses the specified row.
9702 * @param {Number} row The row index
9704 focusRow : function(row)
9706 //Roo.log('GridView.focusRow');
9707 var x = this.bodyEl.dom.scrollLeft;
9708 this.focusCell(row, 0, false);
9709 this.bodyEl.dom.scrollLeft = x;
9713 * Focuses the specified cell.
9714 * @param {Number} row The row index
9715 * @param {Number} col The column index
9716 * @param {Boolean} hscroll false to disable horizontal scrolling
9718 focusCell : function(row, col, hscroll)
9720 //Roo.log('GridView.focusCell');
9721 var el = this.ensureVisible(row, col, hscroll);
9722 // not sure what focusEL achives = it's a <a> pos relative
9723 //this.focusEl.alignTo(el, "tl-tl");
9725 // this.focusEl.focus();
9727 // this.focusEl.focus.defer(1, this.focusEl);
9732 * Scrolls the specified cell into view
9733 * @param {Number} row The row index
9734 * @param {Number} col The column index
9735 * @param {Boolean} hscroll false to disable horizontal scrolling
9737 ensureVisible : function(row, col, hscroll)
9739 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9740 //return null; //disable for testing.
9741 if(typeof row != "number"){
9744 if(row < 0 && row >= this.ds.getCount()){
9747 col = (col !== undefined ? col : 0);
9749 while(cm.isHidden(col)){
9753 var el = this.getCellDom(row, col);
9757 var c = this.bodyEl.dom;
9759 var ctop = parseInt(el.offsetTop, 10);
9760 var cleft = parseInt(el.offsetLeft, 10);
9761 var cbot = ctop + el.offsetHeight;
9762 var cright = cleft + el.offsetWidth;
9764 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9765 var ch = 0; //?? header is not withing the area?
9766 var stop = parseInt(c.scrollTop, 10);
9767 var sleft = parseInt(c.scrollLeft, 10);
9768 var sbot = stop + ch;
9769 var sright = sleft + c.clientWidth;
9771 Roo.log('GridView.ensureVisible:' +
9773 ' c.clientHeight:' + c.clientHeight +
9774 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9783 //Roo.log("set scrolltop to ctop DISABLE?");
9784 }else if(cbot > sbot){
9785 //Roo.log("set scrolltop to cbot-ch");
9786 c.scrollTop = cbot-ch;
9789 if(hscroll !== false){
9791 c.scrollLeft = cleft;
9792 }else if(cright > sright){
9793 c.scrollLeft = cright-c.clientWidth;
9801 insertRow : function(dm, rowIndex, isUpdate){
9804 this.fireEvent("beforerowsinserted", this, rowIndex);
9806 //var s = this.getScrollState();
9807 var row = this.renderRow(this.cm, this.store, rowIndex);
9808 // insert before rowIndex..
9809 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9813 if(row.cellObjects.length){
9814 Roo.each(row.cellObjects, function(r){
9815 _this.renderCellObject(r);
9820 this.fireEvent("rowsinserted", this, rowIndex);
9821 //this.syncRowHeights(firstRow, lastRow);
9822 //this.stripeRows(firstRow);
9829 getRowDom : function(rowIndex)
9831 var rows = this.el.select('tbody > tr', true).elements;
9833 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9836 getCellDom : function(rowIndex, colIndex)
9838 var row = this.getRowDom(rowIndex);
9839 if (row === false) {
9842 var cols = row.select('td', true).elements;
9843 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9847 // returns the object tree for a tr..
9850 renderRow : function(cm, ds, rowIndex)
9852 var d = ds.getAt(rowIndex);
9856 cls : 'x-row-' + rowIndex,
9860 var cellObjects = [];
9862 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9863 var config = cm.config[i];
9865 var renderer = cm.getRenderer(i);
9869 if(typeof(renderer) !== 'undefined'){
9870 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9872 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9873 // and are rendered into the cells after the row is rendered - using the id for the element.
9875 if(typeof(value) === 'object'){
9885 rowIndex : rowIndex,
9890 this.fireEvent('rowclass', this, rowcfg);
9894 // this might end up displaying HTML?
9895 // this is too messy... - better to only do it on columsn you know are going to be too long
9896 //tooltip : (typeof(value) === 'object') ? '' : value,
9897 cls : rowcfg.rowClass + ' x-col-' + i,
9899 html: (typeof(value) === 'object') ? '' : value
9906 if(typeof(config.colspan) != 'undefined'){
9907 td.colspan = config.colspan;
9912 if(typeof(config.align) != 'undefined' && config.align.length){
9913 td.style += ' text-align:' + config.align + ';';
9915 if(typeof(config.valign) != 'undefined' && config.valign.length){
9916 td.style += ' vertical-align:' + config.valign + ';';
9919 if(typeof(config.width) != 'undefined'){
9920 td.style += ' width:' + config.width + 'px;';
9924 if(typeof(config.cursor) != 'undefined'){
9925 td.style += ' cursor:' + config.cursor + ';';
9928 if(typeof(config.cls) != 'undefined'){
9929 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9931 if (this.responsive) {
9932 ['xs','sm','md','lg'].map(function(size){
9934 if(typeof(config[size]) == 'undefined'){
9940 if (!config[size]) { // 0 = hidden
9941 // BS 4 '0' is treated as hide that column and below.
9942 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9946 td.cls += ' col-' + size + '-' + config[size] + (
9947 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9957 row.cellObjects = cellObjects;
9965 onBeforeLoad : function()
9974 this.el.select('tbody', true).first().dom.innerHTML = '';
9977 * Show or hide a row.
9978 * @param {Number} rowIndex to show or hide
9979 * @param {Boolean} state hide
9981 setRowVisibility : function(rowIndex, state)
9983 var bt = this.bodyEl.dom;
9985 var rows = this.el.select('tbody > tr', true).elements;
9987 if(typeof(rows[rowIndex]) == 'undefined'){
9990 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9995 getSelectionModel : function(){
9997 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9999 return this.selModel;
10002 * Render the Roo.bootstrap object from renderder
10004 renderCellObject : function(r)
10008 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10010 var t = r.cfg.render(r.container);
10013 Roo.each(r.cfg.cn, function(c){
10015 container: t.getChildContainer(),
10018 _this.renderCellObject(child);
10023 * get the Row Index from a dom element.
10024 * @param {Roo.Element} row The row to look for
10025 * @returns {Number} the row
10027 getRowIndex : function(row)
10031 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10042 * get the header TH element for columnIndex
10043 * @param {Number} columnIndex
10044 * @returns {Roo.Element}
10046 getHeaderIndex: function(colIndex)
10048 var cols = this.headEl.select('th', true).elements;
10049 return cols[colIndex];
10052 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10053 * @param {domElement} cell to look for
10054 * @returns {Number} the column
10056 getCellIndex : function(cell)
10058 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10060 return parseInt(id[1], 10);
10065 * Returns the grid's underlying element = used by panel.Grid
10066 * @return {Element} The element
10068 getGridEl : function(){
10072 * Forces a resize - used by panel.Grid
10073 * @return {Element} The element
10075 autoSize : function()
10077 //var ctr = Roo.get(this.container.dom.parentElement);
10078 var ctr = Roo.get(this.el.dom);
10080 var thd = this.getGridEl().select('thead',true).first();
10081 var tbd = this.getGridEl().select('tbody', true).first();
10082 var tfd = this.getGridEl().select('tfoot', true).first();
10084 var cw = ctr.getWidth();
10085 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10089 tbd.setWidth(ctr.getWidth());
10090 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10091 // this needs fixing for various usage - currently only hydra job advers I think..
10093 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10095 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10098 cw = Math.max(cw, this.totalWidth);
10099 this.getGridEl().select('tbody tr',true).setWidth(cw);
10102 // resize 'expandable coloumn?
10104 return; // we doe not have a view in this design..
10107 onBodyScroll: function()
10109 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10111 this.headEl.setStyle({
10112 'position' : 'relative',
10113 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10119 var scrollHeight = this.bodyEl.dom.scrollHeight;
10121 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10123 var height = this.bodyEl.getHeight();
10125 if(scrollHeight - height == scrollTop) {
10127 var total = this.ds.getTotalCount();
10129 if(this.footer.cursor + this.footer.pageSize < total){
10131 this.footer.ds.load({
10133 start : this.footer.cursor + this.footer.pageSize,
10134 limit : this.footer.pageSize
10143 onColumnSplitterMoved : function(i, diff)
10145 this.userResized = true;
10147 var cm = this.colModel;
10149 var w = this.getHeaderIndex(i).getWidth() + diff;
10152 cm.setColumnWidth(i, w, true);
10154 //var cid = cm.getColumnId(i); << not used in this version?
10155 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10157 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10158 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10159 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10161 //this.updateSplitters();
10162 //this.layout(); << ??
10163 this.fireEvent("columnresize", i, w);
10165 onHeaderChange : function()
10167 var header = this.renderHeader();
10168 var table = this.el.select('table', true).first();
10170 this.headEl.remove();
10171 this.headEl = table.createChild(header, this.bodyEl, false);
10173 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10174 e.on('click', this.sort, this);
10177 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10178 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10183 onHiddenChange : function(colModel, colIndex, hidden)
10186 this.cm.setHidden()
10187 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10188 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10190 this.CSS.updateRule(thSelector, "display", "");
10191 this.CSS.updateRule(tdSelector, "display", "");
10194 this.CSS.updateRule(thSelector, "display", "none");
10195 this.CSS.updateRule(tdSelector, "display", "none");
10198 // onload calls initCSS()
10199 this.onHeaderChange();
10203 setColumnWidth: function(col_index, width)
10205 // width = "md-2 xs-2..."
10206 if(!this.colModel.config[col_index]) {
10210 var w = width.split(" ");
10212 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10214 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10217 for(var j = 0; j < w.length; j++) {
10223 var size_cls = w[j].split("-");
10225 if(!Number.isInteger(size_cls[1] * 1)) {
10229 if(!this.colModel.config[col_index][size_cls[0]]) {
10233 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10237 h_row[0].classList.replace(
10238 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10239 "col-"+size_cls[0]+"-"+size_cls[1]
10242 for(var i = 0; i < rows.length; i++) {
10244 var size_cls = w[j].split("-");
10246 if(!Number.isInteger(size_cls[1] * 1)) {
10250 if(!this.colModel.config[col_index][size_cls[0]]) {
10254 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10258 rows[i].classList.replace(
10259 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10260 "col-"+size_cls[0]+"-"+size_cls[1]
10264 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10269 // currently only used to find the split on drag..
10270 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10275 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10276 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10285 * @class Roo.bootstrap.TableCell
10286 * @extends Roo.bootstrap.Component
10287 * Bootstrap TableCell class
10288 * @cfg {String} html cell contain text
10289 * @cfg {String} cls cell class
10290 * @cfg {String} tag cell tag (td|th) default td
10291 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10292 * @cfg {String} align Aligns the content in a cell
10293 * @cfg {String} axis Categorizes cells
10294 * @cfg {String} bgcolor Specifies the background color of a cell
10295 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10296 * @cfg {Number} colspan Specifies the number of columns a cell should span
10297 * @cfg {String} headers Specifies one or more header cells a cell is related to
10298 * @cfg {Number} height Sets the height of a cell
10299 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10300 * @cfg {Number} rowspan Sets the number of rows a cell should span
10301 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10302 * @cfg {String} valign Vertical aligns the content in a cell
10303 * @cfg {Number} width Specifies the width of a cell
10306 * Create a new TableCell
10307 * @param {Object} config The config object
10310 Roo.bootstrap.TableCell = function(config){
10311 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10314 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10334 getAutoCreate : function(){
10335 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10342 cfg.tag = this.tag;
10355 cfg.align=this.align
10360 if (this.bgcolor) {
10361 cfg.bgcolor=this.bgcolor
10363 if (this.charoff) {
10364 cfg.charoff=this.charoff
10366 if (this.colspan) {
10367 cfg.colspan=this.colspan
10369 if (this.headers) {
10370 cfg.headers=this.headers
10373 cfg.height=this.height
10376 cfg.nowrap=this.nowrap
10378 if (this.rowspan) {
10379 cfg.rowspan=this.rowspan
10382 cfg.scope=this.scope
10385 cfg.valign=this.valign
10388 cfg.width=this.width
10407 * @class Roo.bootstrap.TableRow
10408 * @extends Roo.bootstrap.Component
10409 * Bootstrap TableRow class
10410 * @cfg {String} cls row class
10411 * @cfg {String} align Aligns the content in a table row
10412 * @cfg {String} bgcolor Specifies a background color for a table row
10413 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10414 * @cfg {String} valign Vertical aligns the content in a table row
10417 * Create a new TableRow
10418 * @param {Object} config The config object
10421 Roo.bootstrap.TableRow = function(config){
10422 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10425 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10433 getAutoCreate : function(){
10434 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10441 cfg.cls = this.cls;
10444 cfg.align = this.align;
10447 cfg.bgcolor = this.bgcolor;
10450 cfg.charoff = this.charoff;
10453 cfg.valign = this.valign;
10471 * @class Roo.bootstrap.TableBody
10472 * @extends Roo.bootstrap.Component
10473 * Bootstrap TableBody class
10474 * @cfg {String} cls element class
10475 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10476 * @cfg {String} align Aligns the content inside the element
10477 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10478 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10481 * Create a new TableBody
10482 * @param {Object} config The config object
10485 Roo.bootstrap.TableBody = function(config){
10486 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10489 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10497 getAutoCreate : function(){
10498 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10508 cfg.tag = this.tag;
10512 cfg.align = this.align;
10515 cfg.charoff = this.charoff;
10518 cfg.valign = this.valign;
10525 // initEvents : function()
10528 // if(!this.store){
10532 // this.store = Roo.factory(this.store, Roo.data);
10533 // this.store.on('load', this.onLoad, this);
10535 // this.store.load();
10539 // onLoad: function ()
10541 // this.fireEvent('load', this);
10551 * Ext JS Library 1.1.1
10552 * Copyright(c) 2006-2007, Ext JS, LLC.
10554 * Originally Released Under LGPL - original licence link has changed is not relivant.
10557 * <script type="text/javascript">
10560 // as we use this in bootstrap.
10561 Roo.namespace('Roo.form');
10563 * @class Roo.form.Action
10564 * Internal Class used to handle form actions
10566 * @param {Roo.form.BasicForm} el The form element or its id
10567 * @param {Object} config Configuration options
10572 // define the action interface
10573 Roo.form.Action = function(form, options){
10575 this.options = options || {};
10578 * Client Validation Failed
10581 Roo.form.Action.CLIENT_INVALID = 'client';
10583 * Server Validation Failed
10586 Roo.form.Action.SERVER_INVALID = 'server';
10588 * Connect to Server Failed
10591 Roo.form.Action.CONNECT_FAILURE = 'connect';
10593 * Reading Data from Server Failed
10596 Roo.form.Action.LOAD_FAILURE = 'load';
10598 Roo.form.Action.prototype = {
10600 failureType : undefined,
10601 response : undefined,
10602 result : undefined,
10604 // interface method
10605 run : function(options){
10609 // interface method
10610 success : function(response){
10614 // interface method
10615 handleResponse : function(response){
10619 // default connection failure
10620 failure : function(response){
10622 this.response = response;
10623 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10624 this.form.afterAction(this, false);
10627 processResponse : function(response){
10628 this.response = response;
10629 if(!response.responseText){
10632 this.result = this.handleResponse(response);
10633 return this.result;
10636 // utility functions used internally
10637 getUrl : function(appendParams){
10638 var url = this.options.url || this.form.url || this.form.el.dom.action;
10640 var p = this.getParams();
10642 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10648 getMethod : function(){
10649 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10652 getParams : function(){
10653 var bp = this.form.baseParams;
10654 var p = this.options.params;
10656 if(typeof p == "object"){
10657 p = Roo.urlEncode(Roo.applyIf(p, bp));
10658 }else if(typeof p == 'string' && bp){
10659 p += '&' + Roo.urlEncode(bp);
10662 p = Roo.urlEncode(bp);
10667 createCallback : function(){
10669 success: this.success,
10670 failure: this.failure,
10672 timeout: (this.form.timeout*1000),
10673 upload: this.form.fileUpload ? this.success : undefined
10678 Roo.form.Action.Submit = function(form, options){
10679 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10682 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10685 haveProgress : false,
10686 uploadComplete : false,
10688 // uploadProgress indicator.
10689 uploadProgress : function()
10691 if (!this.form.progressUrl) {
10695 if (!this.haveProgress) {
10696 Roo.MessageBox.progress("Uploading", "Uploading");
10698 if (this.uploadComplete) {
10699 Roo.MessageBox.hide();
10703 this.haveProgress = true;
10705 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10707 var c = new Roo.data.Connection();
10709 url : this.form.progressUrl,
10714 success : function(req){
10715 //console.log(data);
10719 rdata = Roo.decode(req.responseText)
10721 Roo.log("Invalid data from server..");
10725 if (!rdata || !rdata.success) {
10727 Roo.MessageBox.alert(Roo.encode(rdata));
10730 var data = rdata.data;
10732 if (this.uploadComplete) {
10733 Roo.MessageBox.hide();
10738 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10739 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10742 this.uploadProgress.defer(2000,this);
10745 failure: function(data) {
10746 Roo.log('progress url failed ');
10757 // run get Values on the form, so it syncs any secondary forms.
10758 this.form.getValues();
10760 var o = this.options;
10761 var method = this.getMethod();
10762 var isPost = method == 'POST';
10763 if(o.clientValidation === false || this.form.isValid()){
10765 if (this.form.progressUrl) {
10766 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10767 (new Date() * 1) + '' + Math.random());
10772 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10773 form:this.form.el.dom,
10774 url:this.getUrl(!isPost),
10776 params:isPost ? this.getParams() : null,
10777 isUpload: this.form.fileUpload,
10778 formData : this.form.formData
10781 this.uploadProgress();
10783 }else if (o.clientValidation !== false){ // client validation failed
10784 this.failureType = Roo.form.Action.CLIENT_INVALID;
10785 this.form.afterAction(this, false);
10789 success : function(response)
10791 this.uploadComplete= true;
10792 if (this.haveProgress) {
10793 Roo.MessageBox.hide();
10797 var result = this.processResponse(response);
10798 if(result === true || result.success){
10799 this.form.afterAction(this, true);
10803 this.form.markInvalid(result.errors);
10804 this.failureType = Roo.form.Action.SERVER_INVALID;
10806 this.form.afterAction(this, false);
10808 failure : function(response)
10810 this.uploadComplete= true;
10811 if (this.haveProgress) {
10812 Roo.MessageBox.hide();
10815 this.response = response;
10816 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10817 this.form.afterAction(this, false);
10820 handleResponse : function(response){
10821 if(this.form.errorReader){
10822 var rs = this.form.errorReader.read(response);
10825 for(var i = 0, len = rs.records.length; i < len; i++) {
10826 var r = rs.records[i];
10827 errors[i] = r.data;
10830 if(errors.length < 1){
10834 success : rs.success,
10840 ret = Roo.decode(response.responseText);
10844 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10854 Roo.form.Action.Load = function(form, options){
10855 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10856 this.reader = this.form.reader;
10859 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10864 Roo.Ajax.request(Roo.apply(
10865 this.createCallback(), {
10866 method:this.getMethod(),
10867 url:this.getUrl(false),
10868 params:this.getParams()
10872 success : function(response){
10874 var result = this.processResponse(response);
10875 if(result === true || !result.success || !result.data){
10876 this.failureType = Roo.form.Action.LOAD_FAILURE;
10877 this.form.afterAction(this, false);
10880 this.form.clearInvalid();
10881 this.form.setValues(result.data);
10882 this.form.afterAction(this, true);
10885 handleResponse : function(response){
10886 if(this.form.reader){
10887 var rs = this.form.reader.read(response);
10888 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10890 success : rs.success,
10894 return Roo.decode(response.responseText);
10898 Roo.form.Action.ACTION_TYPES = {
10899 'load' : Roo.form.Action.Load,
10900 'submit' : Roo.form.Action.Submit
10909 * @class Roo.bootstrap.Form
10910 * @extends Roo.bootstrap.Component
10911 * Bootstrap Form class
10912 * @cfg {String} method GET | POST (default POST)
10913 * @cfg {String} labelAlign top | left (default top)
10914 * @cfg {String} align left | right - for navbars
10915 * @cfg {Boolean} loadMask load mask when submit (default true)
10919 * Create a new Form
10920 * @param {Object} config The config object
10924 Roo.bootstrap.Form = function(config){
10926 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10928 Roo.bootstrap.Form.popover.apply();
10932 * @event clientvalidation
10933 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10934 * @param {Form} this
10935 * @param {Boolean} valid true if the form has passed client-side validation
10937 clientvalidation: true,
10939 * @event beforeaction
10940 * Fires before any action is performed. Return false to cancel the action.
10941 * @param {Form} this
10942 * @param {Action} action The action to be performed
10944 beforeaction: true,
10946 * @event actionfailed
10947 * Fires when an action fails.
10948 * @param {Form} this
10949 * @param {Action} action The action that failed
10951 actionfailed : true,
10953 * @event actioncomplete
10954 * Fires when an action is completed.
10955 * @param {Form} this
10956 * @param {Action} action The action that completed
10958 actioncomplete : true
10962 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10965 * @cfg {String} method
10966 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10970 * @cfg {String} url
10971 * The URL to use for form actions if one isn't supplied in the action options.
10974 * @cfg {Boolean} fileUpload
10975 * Set to true if this form is a file upload.
10979 * @cfg {Object} baseParams
10980 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10984 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10988 * @cfg {Sting} align (left|right) for navbar forms
10993 activeAction : null,
10996 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10997 * element by passing it or its id or mask the form itself by passing in true.
11000 waitMsgTarget : false,
11005 * @cfg {Boolean} errorMask (true|false) default false
11010 * @cfg {Number} maskOffset Default 100
11015 * @cfg {Boolean} maskBody
11019 getAutoCreate : function(){
11023 method : this.method || 'POST',
11024 id : this.id || Roo.id(),
11027 if (this.parent().xtype.match(/^Nav/)) {
11028 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11032 if (this.labelAlign == 'left' ) {
11033 cfg.cls += ' form-horizontal';
11039 initEvents : function()
11041 this.el.on('submit', this.onSubmit, this);
11042 // this was added as random key presses on the form where triggering form submit.
11043 this.el.on('keypress', function(e) {
11044 if (e.getCharCode() != 13) {
11047 // we might need to allow it for textareas.. and some other items.
11048 // check e.getTarget().
11050 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11054 Roo.log("keypress blocked");
11056 e.preventDefault();
11062 onSubmit : function(e){
11067 * Returns true if client-side validation on the form is successful.
11070 isValid : function(){
11071 var items = this.getItems();
11073 var target = false;
11075 items.each(function(f){
11081 Roo.log('invalid field: ' + f.name);
11085 if(!target && f.el.isVisible(true)){
11091 if(this.errorMask && !valid){
11092 Roo.bootstrap.Form.popover.mask(this, target);
11099 * Returns true if any fields in this form have changed since their original load.
11102 isDirty : function(){
11104 var items = this.getItems();
11105 items.each(function(f){
11115 * Performs a predefined action (submit or load) or custom actions you define on this form.
11116 * @param {String} actionName The name of the action type
11117 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11118 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11119 * accept other config options):
11121 Property Type Description
11122 ---------------- --------------- ----------------------------------------------------------------------------------
11123 url String The url for the action (defaults to the form's url)
11124 method String The form method to use (defaults to the form's method, or POST if not defined)
11125 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11126 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11127 validate the form on the client (defaults to false)
11129 * @return {BasicForm} this
11131 doAction : function(action, options){
11132 if(typeof action == 'string'){
11133 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11135 if(this.fireEvent('beforeaction', this, action) !== false){
11136 this.beforeAction(action);
11137 action.run.defer(100, action);
11143 beforeAction : function(action){
11144 var o = action.options;
11149 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11151 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11154 // not really supported yet.. ??
11156 //if(this.waitMsgTarget === true){
11157 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11158 //}else if(this.waitMsgTarget){
11159 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11160 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11162 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11168 afterAction : function(action, success){
11169 this.activeAction = null;
11170 var o = action.options;
11175 Roo.get(document.body).unmask();
11181 //if(this.waitMsgTarget === true){
11182 // this.el.unmask();
11183 //}else if(this.waitMsgTarget){
11184 // this.waitMsgTarget.unmask();
11186 // Roo.MessageBox.updateProgress(1);
11187 // Roo.MessageBox.hide();
11194 Roo.callback(o.success, o.scope, [this, action]);
11195 this.fireEvent('actioncomplete', this, action);
11199 // failure condition..
11200 // we have a scenario where updates need confirming.
11201 // eg. if a locking scenario exists..
11202 // we look for { errors : { needs_confirm : true }} in the response.
11204 (typeof(action.result) != 'undefined') &&
11205 (typeof(action.result.errors) != 'undefined') &&
11206 (typeof(action.result.errors.needs_confirm) != 'undefined')
11209 Roo.log("not supported yet");
11212 Roo.MessageBox.confirm(
11213 "Change requires confirmation",
11214 action.result.errorMsg,
11219 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11229 Roo.callback(o.failure, o.scope, [this, action]);
11230 // show an error message if no failed handler is set..
11231 if (!this.hasListener('actionfailed')) {
11232 Roo.log("need to add dialog support");
11234 Roo.MessageBox.alert("Error",
11235 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11236 action.result.errorMsg :
11237 "Saving Failed, please check your entries or try again"
11242 this.fireEvent('actionfailed', this, action);
11247 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11248 * @param {String} id The value to search for
11251 findField : function(id){
11252 var items = this.getItems();
11253 var field = items.get(id);
11255 items.each(function(f){
11256 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11263 return field || null;
11266 * Mark fields in this form invalid in bulk.
11267 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11268 * @return {BasicForm} this
11270 markInvalid : function(errors){
11271 if(errors instanceof Array){
11272 for(var i = 0, len = errors.length; i < len; i++){
11273 var fieldError = errors[i];
11274 var f = this.findField(fieldError.id);
11276 f.markInvalid(fieldError.msg);
11282 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11283 field.markInvalid(errors[id]);
11287 //Roo.each(this.childForms || [], function (f) {
11288 // f.markInvalid(errors);
11295 * Set values for fields in this form in bulk.
11296 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11297 * @return {BasicForm} this
11299 setValues : function(values){
11300 if(values instanceof Array){ // array of objects
11301 for(var i = 0, len = values.length; i < len; i++){
11303 var f = this.findField(v.id);
11305 f.setValue(v.value);
11306 if(this.trackResetOnLoad){
11307 f.originalValue = f.getValue();
11311 }else{ // object hash
11314 if(typeof values[id] != 'function' && (field = this.findField(id))){
11316 if (field.setFromData &&
11317 field.valueField &&
11318 field.displayField &&
11319 // combos' with local stores can
11320 // be queried via setValue()
11321 // to set their value..
11322 (field.store && !field.store.isLocal)
11326 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11327 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11328 field.setFromData(sd);
11330 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11332 field.setFromData(values);
11335 field.setValue(values[id]);
11339 if(this.trackResetOnLoad){
11340 field.originalValue = field.getValue();
11346 //Roo.each(this.childForms || [], function (f) {
11347 // f.setValues(values);
11354 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11355 * they are returned as an array.
11356 * @param {Boolean} asString
11359 getValues : function(asString){
11360 //if (this.childForms) {
11361 // copy values from the child forms
11362 // Roo.each(this.childForms, function (f) {
11363 // this.setValues(f.getValues());
11369 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11370 if(asString === true){
11373 return Roo.urlDecode(fs);
11377 * Returns the fields in this form as an object with key/value pairs.
11378 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11381 getFieldValues : function(with_hidden)
11383 var items = this.getItems();
11385 items.each(function(f){
11387 if (!f.getName()) {
11391 var v = f.getValue();
11393 if (f.inputType =='radio') {
11394 if (typeof(ret[f.getName()]) == 'undefined') {
11395 ret[f.getName()] = ''; // empty..
11398 if (!f.el.dom.checked) {
11402 v = f.el.dom.value;
11406 if(f.xtype == 'MoneyField'){
11407 ret[f.currencyName] = f.getCurrency();
11410 // not sure if this supported any more..
11411 if ((typeof(v) == 'object') && f.getRawValue) {
11412 v = f.getRawValue() ; // dates..
11414 // combo boxes where name != hiddenName...
11415 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11416 ret[f.name] = f.getRawValue();
11418 ret[f.getName()] = v;
11425 * Clears all invalid messages in this form.
11426 * @return {BasicForm} this
11428 clearInvalid : function(){
11429 var items = this.getItems();
11431 items.each(function(f){
11439 * Resets this form.
11440 * @return {BasicForm} this
11442 reset : function(){
11443 var items = this.getItems();
11444 items.each(function(f){
11448 Roo.each(this.childForms || [], function (f) {
11456 getItems : function()
11458 var r=new Roo.util.MixedCollection(false, function(o){
11459 return o.id || (o.id = Roo.id());
11461 var iter = function(el) {
11468 Roo.each(el.items,function(e) {
11477 hideFields : function(items)
11479 Roo.each(items, function(i){
11481 var f = this.findField(i);
11492 showFields : function(items)
11494 Roo.each(items, function(i){
11496 var f = this.findField(i);
11509 Roo.apply(Roo.bootstrap.Form, {
11525 intervalID : false,
11531 if(this.isApplied){
11536 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11537 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11538 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11539 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11542 this.maskEl.top.enableDisplayMode("block");
11543 this.maskEl.left.enableDisplayMode("block");
11544 this.maskEl.bottom.enableDisplayMode("block");
11545 this.maskEl.right.enableDisplayMode("block");
11547 this.toolTip = new Roo.bootstrap.Tooltip({
11548 cls : 'roo-form-error-popover',
11550 'left' : ['r-l', [-2,0], 'right'],
11551 'right' : ['l-r', [2,0], 'left'],
11552 'bottom' : ['tl-bl', [0,2], 'top'],
11553 'top' : [ 'bl-tl', [0,-2], 'bottom']
11557 this.toolTip.render(Roo.get(document.body));
11559 this.toolTip.el.enableDisplayMode("block");
11561 Roo.get(document.body).on('click', function(){
11565 Roo.get(document.body).on('touchstart', function(){
11569 this.isApplied = true
11572 mask : function(form, target)
11576 this.target = target;
11578 if(!this.form.errorMask || !target.el){
11582 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11584 Roo.log(scrollable);
11586 var ot = this.target.el.calcOffsetsTo(scrollable);
11588 var scrollTo = ot[1] - this.form.maskOffset;
11590 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11592 scrollable.scrollTo('top', scrollTo);
11594 var box = this.target.el.getBox();
11596 var zIndex = Roo.bootstrap.Modal.zIndex++;
11599 this.maskEl.top.setStyle('position', 'absolute');
11600 this.maskEl.top.setStyle('z-index', zIndex);
11601 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11602 this.maskEl.top.setLeft(0);
11603 this.maskEl.top.setTop(0);
11604 this.maskEl.top.show();
11606 this.maskEl.left.setStyle('position', 'absolute');
11607 this.maskEl.left.setStyle('z-index', zIndex);
11608 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11609 this.maskEl.left.setLeft(0);
11610 this.maskEl.left.setTop(box.y - this.padding);
11611 this.maskEl.left.show();
11613 this.maskEl.bottom.setStyle('position', 'absolute');
11614 this.maskEl.bottom.setStyle('z-index', zIndex);
11615 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11616 this.maskEl.bottom.setLeft(0);
11617 this.maskEl.bottom.setTop(box.bottom + this.padding);
11618 this.maskEl.bottom.show();
11620 this.maskEl.right.setStyle('position', 'absolute');
11621 this.maskEl.right.setStyle('z-index', zIndex);
11622 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11623 this.maskEl.right.setLeft(box.right + this.padding);
11624 this.maskEl.right.setTop(box.y - this.padding);
11625 this.maskEl.right.show();
11627 this.toolTip.bindEl = this.target.el;
11629 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11631 var tip = this.target.blankText;
11633 if(this.target.getValue() !== '' ) {
11635 if (this.target.invalidText.length) {
11636 tip = this.target.invalidText;
11637 } else if (this.target.regexText.length){
11638 tip = this.target.regexText;
11642 this.toolTip.show(tip);
11644 this.intervalID = window.setInterval(function() {
11645 Roo.bootstrap.Form.popover.unmask();
11648 window.onwheel = function(){ return false;};
11650 (function(){ this.isMasked = true; }).defer(500, this);
11654 unmask : function()
11656 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11660 this.maskEl.top.setStyle('position', 'absolute');
11661 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11662 this.maskEl.top.hide();
11664 this.maskEl.left.setStyle('position', 'absolute');
11665 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11666 this.maskEl.left.hide();
11668 this.maskEl.bottom.setStyle('position', 'absolute');
11669 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11670 this.maskEl.bottom.hide();
11672 this.maskEl.right.setStyle('position', 'absolute');
11673 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11674 this.maskEl.right.hide();
11676 this.toolTip.hide();
11678 this.toolTip.el.hide();
11680 window.onwheel = function(){ return true;};
11682 if(this.intervalID){
11683 window.clearInterval(this.intervalID);
11684 this.intervalID = false;
11687 this.isMasked = false;
11697 * Ext JS Library 1.1.1
11698 * Copyright(c) 2006-2007, Ext JS, LLC.
11700 * Originally Released Under LGPL - original licence link has changed is not relivant.
11703 * <script type="text/javascript">
11706 * @class Roo.form.VTypes
11707 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11710 Roo.form.VTypes = function(){
11711 // closure these in so they are only created once.
11712 var alpha = /^[a-zA-Z_]+$/;
11713 var alphanum = /^[a-zA-Z0-9_]+$/;
11714 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11715 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11717 // All these messages and functions are configurable
11720 * The function used to validate email addresses
11721 * @param {String} value The email address
11723 'email' : function(v){
11724 return email.test(v);
11727 * The error text to display when the email validation function returns false
11730 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11732 * The keystroke filter mask to be applied on email input
11735 'emailMask' : /[a-z0-9_\.\-@]/i,
11738 * The function used to validate URLs
11739 * @param {String} value The URL
11741 'url' : function(v){
11742 return url.test(v);
11745 * The error text to display when the url validation function returns false
11748 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11751 * The function used to validate alpha values
11752 * @param {String} value The value
11754 'alpha' : function(v){
11755 return alpha.test(v);
11758 * The error text to display when the alpha validation function returns false
11761 'alphaText' : 'This field should only contain letters and _',
11763 * The keystroke filter mask to be applied on alpha input
11766 'alphaMask' : /[a-z_]/i,
11769 * The function used to validate alphanumeric values
11770 * @param {String} value The value
11772 'alphanum' : function(v){
11773 return alphanum.test(v);
11776 * The error text to display when the alphanumeric validation function returns false
11779 'alphanumText' : 'This field should only contain letters, numbers and _',
11781 * The keystroke filter mask to be applied on alphanumeric input
11784 'alphanumMask' : /[a-z0-9_]/i
11794 * @class Roo.bootstrap.Input
11795 * @extends Roo.bootstrap.Component
11796 * Bootstrap Input class
11797 * @cfg {Boolean} disabled is it disabled
11798 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11799 * @cfg {String} name name of the input
11800 * @cfg {string} fieldLabel - the label associated
11801 * @cfg {string} placeholder - placeholder to put in text.
11802 * @cfg {string} before - input group add on before
11803 * @cfg {string} after - input group add on after
11804 * @cfg {string} size - (lg|sm) or leave empty..
11805 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11806 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11807 * @cfg {Number} md colspan out of 12 for computer-sized screens
11808 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11809 * @cfg {string} value default value of the input
11810 * @cfg {Number} labelWidth set the width of label
11811 * @cfg {Number} labellg set the width of label (1-12)
11812 * @cfg {Number} labelmd set the width of label (1-12)
11813 * @cfg {Number} labelsm set the width of label (1-12)
11814 * @cfg {Number} labelxs set the width of label (1-12)
11815 * @cfg {String} labelAlign (top|left)
11816 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11817 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11818 * @cfg {String} indicatorpos (left|right) default left
11819 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11820 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11821 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11823 * @cfg {String} align (left|center|right) Default left
11824 * @cfg {Boolean} forceFeedback (true|false) Default false
11827 * Create a new Input
11828 * @param {Object} config The config object
11831 Roo.bootstrap.Input = function(config){
11833 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11838 * Fires when this field receives input focus.
11839 * @param {Roo.form.Field} this
11844 * Fires when this field loses input focus.
11845 * @param {Roo.form.Field} this
11849 * @event specialkey
11850 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11851 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11852 * @param {Roo.form.Field} this
11853 * @param {Roo.EventObject} e The event object
11858 * Fires just before the field blurs if the field value has changed.
11859 * @param {Roo.form.Field} this
11860 * @param {Mixed} newValue The new value
11861 * @param {Mixed} oldValue The original value
11866 * Fires after the field has been marked as invalid.
11867 * @param {Roo.form.Field} this
11868 * @param {String} msg The validation message
11873 * Fires after the field has been validated with no errors.
11874 * @param {Roo.form.Field} this
11879 * Fires after the key up
11880 * @param {Roo.form.Field} this
11881 * @param {Roo.EventObject} e The event Object
11886 * Fires after the user pastes into input
11887 * @param {Roo.form.Field} this
11888 * @param {Roo.EventObject} e The event Object
11894 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11896 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11897 automatic validation (defaults to "keyup").
11899 validationEvent : "keyup",
11901 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11903 validateOnBlur : true,
11905 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11907 validationDelay : 250,
11909 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11911 focusClass : "x-form-focus", // not needed???
11915 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11917 invalidClass : "has-warning",
11920 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11922 validClass : "has-success",
11925 * @cfg {Boolean} hasFeedback (true|false) default true
11927 hasFeedback : true,
11930 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11932 invalidFeedbackClass : "glyphicon-warning-sign",
11935 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11937 validFeedbackClass : "glyphicon-ok",
11940 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11942 selectOnFocus : false,
11945 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11949 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11954 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11956 disableKeyFilter : false,
11959 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11963 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11967 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11969 blankText : "Please complete this mandatory field",
11972 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11976 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11978 maxLength : Number.MAX_VALUE,
11980 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11982 minLengthText : "The minimum length for this field is {0}",
11984 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11986 maxLengthText : "The maximum length for this field is {0}",
11990 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11991 * If available, this function will be called only after the basic validators all return true, and will be passed the
11992 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11996 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11997 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11998 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12002 * @cfg {String} regexText -- Depricated - use Invalid Text
12007 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12013 autocomplete: false,
12017 inputType : 'text',
12020 placeholder: false,
12025 preventMark: false,
12026 isFormField : true,
12029 labelAlign : false,
12032 formatedValue : false,
12033 forceFeedback : false,
12035 indicatorpos : 'left',
12045 parentLabelAlign : function()
12048 while (parent.parent()) {
12049 parent = parent.parent();
12050 if (typeof(parent.labelAlign) !='undefined') {
12051 return parent.labelAlign;
12058 getAutoCreate : function()
12060 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12066 if(this.inputType != 'hidden'){
12067 cfg.cls = 'form-group' //input-group
12073 type : this.inputType,
12074 value : this.value,
12075 cls : 'form-control',
12076 placeholder : this.placeholder || '',
12077 autocomplete : this.autocomplete || 'new-password'
12079 if (this.inputType == 'file') {
12080 input.style = 'overflow:hidden'; // why not in CSS?
12083 if(this.capture.length){
12084 input.capture = this.capture;
12087 if(this.accept.length){
12088 input.accept = this.accept + "/*";
12092 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12095 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12096 input.maxLength = this.maxLength;
12099 if (this.disabled) {
12100 input.disabled=true;
12103 if (this.readOnly) {
12104 input.readonly=true;
12108 input.name = this.name;
12112 input.cls += ' input-' + this.size;
12116 ['xs','sm','md','lg'].map(function(size){
12117 if (settings[size]) {
12118 cfg.cls += ' col-' + size + '-' + settings[size];
12122 var inputblock = input;
12126 cls: 'glyphicon form-control-feedback'
12129 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12132 cls : 'has-feedback',
12140 if (this.before || this.after) {
12143 cls : 'input-group',
12147 if (this.before && typeof(this.before) == 'string') {
12149 inputblock.cn.push({
12151 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12155 if (this.before && typeof(this.before) == 'object') {
12156 this.before = Roo.factory(this.before);
12158 inputblock.cn.push({
12160 cls : 'roo-input-before input-group-prepend input-group-' +
12161 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12165 inputblock.cn.push(input);
12167 if (this.after && typeof(this.after) == 'string') {
12168 inputblock.cn.push({
12170 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12174 if (this.after && typeof(this.after) == 'object') {
12175 this.after = Roo.factory(this.after);
12177 inputblock.cn.push({
12179 cls : 'roo-input-after input-group-append input-group-' +
12180 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12184 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12185 inputblock.cls += ' has-feedback';
12186 inputblock.cn.push(feedback);
12191 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12192 tooltip : 'This field is required'
12194 if (this.allowBlank ) {
12195 indicator.style = this.allowBlank ? ' display:none' : '';
12197 if (align ==='left' && this.fieldLabel.length) {
12199 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12206 cls : 'control-label col-form-label',
12207 html : this.fieldLabel
12218 var labelCfg = cfg.cn[1];
12219 var contentCfg = cfg.cn[2];
12221 if(this.indicatorpos == 'right'){
12226 cls : 'control-label col-form-label',
12230 html : this.fieldLabel
12244 labelCfg = cfg.cn[0];
12245 contentCfg = cfg.cn[1];
12249 if(this.labelWidth > 12){
12250 labelCfg.style = "width: " + this.labelWidth + 'px';
12253 if(this.labelWidth < 13 && this.labelmd == 0){
12254 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12257 if(this.labellg > 0){
12258 labelCfg.cls += ' col-lg-' + this.labellg;
12259 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12262 if(this.labelmd > 0){
12263 labelCfg.cls += ' col-md-' + this.labelmd;
12264 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12267 if(this.labelsm > 0){
12268 labelCfg.cls += ' col-sm-' + this.labelsm;
12269 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12272 if(this.labelxs > 0){
12273 labelCfg.cls += ' col-xs-' + this.labelxs;
12274 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12278 } else if ( this.fieldLabel.length) {
12285 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12286 tooltip : 'This field is required',
12287 style : this.allowBlank ? ' display:none' : ''
12291 //cls : 'input-group-addon',
12292 html : this.fieldLabel
12300 if(this.indicatorpos == 'right'){
12305 //cls : 'input-group-addon',
12306 html : this.fieldLabel
12311 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12312 tooltip : 'This field is required',
12313 style : this.allowBlank ? ' display:none' : ''
12333 if (this.parentType === 'Navbar' && this.parent().bar) {
12334 cfg.cls += ' navbar-form';
12337 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12338 // on BS4 we do this only if not form
12339 cfg.cls += ' navbar-form';
12347 * return the real input element.
12349 inputEl: function ()
12351 return this.el.select('input.form-control',true).first();
12354 tooltipEl : function()
12356 return this.inputEl();
12359 indicatorEl : function()
12361 if (Roo.bootstrap.version == 4) {
12362 return false; // not enabled in v4 yet.
12365 var indicator = this.el.select('i.roo-required-indicator',true).first();
12375 setDisabled : function(v)
12377 var i = this.inputEl().dom;
12379 i.removeAttribute('disabled');
12383 i.setAttribute('disabled','true');
12385 initEvents : function()
12388 this.inputEl().on("keydown" , this.fireKey, this);
12389 this.inputEl().on("focus", this.onFocus, this);
12390 this.inputEl().on("blur", this.onBlur, this);
12392 this.inputEl().relayEvent('keyup', this);
12393 this.inputEl().relayEvent('paste', this);
12395 this.indicator = this.indicatorEl();
12397 if(this.indicator){
12398 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12401 // reference to original value for reset
12402 this.originalValue = this.getValue();
12403 //Roo.form.TextField.superclass.initEvents.call(this);
12404 if(this.validationEvent == 'keyup'){
12405 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12406 this.inputEl().on('keyup', this.filterValidation, this);
12408 else if(this.validationEvent !== false){
12409 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12412 if(this.selectOnFocus){
12413 this.on("focus", this.preFocus, this);
12416 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12417 this.inputEl().on("keypress", this.filterKeys, this);
12419 this.inputEl().relayEvent('keypress', this);
12422 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12423 this.el.on("click", this.autoSize, this);
12426 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12427 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12430 if (typeof(this.before) == 'object') {
12431 this.before.render(this.el.select('.roo-input-before',true).first());
12433 if (typeof(this.after) == 'object') {
12434 this.after.render(this.el.select('.roo-input-after',true).first());
12437 this.inputEl().on('change', this.onChange, this);
12440 filterValidation : function(e){
12441 if(!e.isNavKeyPress()){
12442 this.validationTask.delay(this.validationDelay);
12446 * Validates the field value
12447 * @return {Boolean} True if the value is valid, else false
12449 validate : function(){
12450 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12451 if(this.disabled || this.validateValue(this.getRawValue())){
12456 this.markInvalid();
12462 * Validates a value according to the field's validation rules and marks the field as invalid
12463 * if the validation fails
12464 * @param {Mixed} value The value to validate
12465 * @return {Boolean} True if the value is valid, else false
12467 validateValue : function(value)
12469 if(this.getVisibilityEl().hasClass('hidden')){
12473 if(value.length < 1) { // if it's blank
12474 if(this.allowBlank){
12480 if(value.length < this.minLength){
12483 if(value.length > this.maxLength){
12487 var vt = Roo.form.VTypes;
12488 if(!vt[this.vtype](value, this)){
12492 if(typeof this.validator == "function"){
12493 var msg = this.validator(value);
12497 if (typeof(msg) == 'string') {
12498 this.invalidText = msg;
12502 if(this.regex && !this.regex.test(value)){
12510 fireKey : function(e){
12511 //Roo.log('field ' + e.getKey());
12512 if(e.isNavKeyPress()){
12513 this.fireEvent("specialkey", this, e);
12516 focus : function (selectText){
12518 this.inputEl().focus();
12519 if(selectText === true){
12520 this.inputEl().dom.select();
12526 onFocus : function(){
12527 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12528 // this.el.addClass(this.focusClass);
12530 if(!this.hasFocus){
12531 this.hasFocus = true;
12532 this.startValue = this.getValue();
12533 this.fireEvent("focus", this);
12537 beforeBlur : Roo.emptyFn,
12541 onBlur : function(){
12543 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12544 //this.el.removeClass(this.focusClass);
12546 this.hasFocus = false;
12547 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12550 var v = this.getValue();
12551 if(String(v) !== String(this.startValue)){
12552 this.fireEvent('change', this, v, this.startValue);
12554 this.fireEvent("blur", this);
12557 onChange : function(e)
12559 var v = this.getValue();
12560 if(String(v) !== String(this.startValue)){
12561 this.fireEvent('change', this, v, this.startValue);
12567 * Resets the current field value to the originally loaded value and clears any validation messages
12569 reset : function(){
12570 this.setValue(this.originalValue);
12574 * Returns the name of the field
12575 * @return {Mixed} name The name field
12577 getName: function(){
12581 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12582 * @return {Mixed} value The field value
12584 getValue : function(){
12586 var v = this.inputEl().getValue();
12591 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12592 * @return {Mixed} value The field value
12594 getRawValue : function(){
12595 var v = this.inputEl().getValue();
12601 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12602 * @param {Mixed} value The value to set
12604 setRawValue : function(v){
12605 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12608 selectText : function(start, end){
12609 var v = this.getRawValue();
12611 start = start === undefined ? 0 : start;
12612 end = end === undefined ? v.length : end;
12613 var d = this.inputEl().dom;
12614 if(d.setSelectionRange){
12615 d.setSelectionRange(start, end);
12616 }else if(d.createTextRange){
12617 var range = d.createTextRange();
12618 range.moveStart("character", start);
12619 range.moveEnd("character", v.length-end);
12626 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12627 * @param {Mixed} value The value to set
12629 setValue : function(v){
12632 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12638 processValue : function(value){
12639 if(this.stripCharsRe){
12640 var newValue = value.replace(this.stripCharsRe, '');
12641 if(newValue !== value){
12642 this.setRawValue(newValue);
12649 preFocus : function(){
12651 if(this.selectOnFocus){
12652 this.inputEl().dom.select();
12655 filterKeys : function(e){
12656 var k = e.getKey();
12657 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12660 var c = e.getCharCode(), cc = String.fromCharCode(c);
12661 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12664 if(!this.maskRe.test(cc)){
12669 * Clear any invalid styles/messages for this field
12671 clearInvalid : function(){
12673 if(!this.el || this.preventMark){ // not rendered
12678 this.el.removeClass([this.invalidClass, 'is-invalid']);
12680 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12682 var feedback = this.el.select('.form-control-feedback', true).first();
12685 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12690 if(this.indicator){
12691 this.indicator.removeClass('visible');
12692 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12695 this.fireEvent('valid', this);
12699 * Mark this field as valid
12701 markValid : function()
12703 if(!this.el || this.preventMark){ // not rendered...
12707 this.el.removeClass([this.invalidClass, this.validClass]);
12708 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12710 var feedback = this.el.select('.form-control-feedback', true).first();
12713 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12716 if(this.indicator){
12717 this.indicator.removeClass('visible');
12718 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12726 if(this.allowBlank && !this.getRawValue().length){
12729 if (Roo.bootstrap.version == 3) {
12730 this.el.addClass(this.validClass);
12732 this.inputEl().addClass('is-valid');
12735 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12737 var feedback = this.el.select('.form-control-feedback', true).first();
12740 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12741 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12746 this.fireEvent('valid', this);
12750 * Mark this field as invalid
12751 * @param {String} msg The validation message
12753 markInvalid : function(msg)
12755 if(!this.el || this.preventMark){ // not rendered
12759 this.el.removeClass([this.invalidClass, this.validClass]);
12760 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12762 var feedback = this.el.select('.form-control-feedback', true).first();
12765 this.el.select('.form-control-feedback', true).first().removeClass(
12766 [this.invalidFeedbackClass, this.validFeedbackClass]);
12773 if(this.allowBlank && !this.getRawValue().length){
12777 if(this.indicator){
12778 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12779 this.indicator.addClass('visible');
12781 if (Roo.bootstrap.version == 3) {
12782 this.el.addClass(this.invalidClass);
12784 this.inputEl().addClass('is-invalid');
12789 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12791 var feedback = this.el.select('.form-control-feedback', true).first();
12794 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12796 if(this.getValue().length || this.forceFeedback){
12797 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12804 this.fireEvent('invalid', this, msg);
12807 SafariOnKeyDown : function(event)
12809 // this is a workaround for a password hang bug on chrome/ webkit.
12810 if (this.inputEl().dom.type != 'password') {
12814 var isSelectAll = false;
12816 if(this.inputEl().dom.selectionEnd > 0){
12817 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12819 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12820 event.preventDefault();
12825 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12827 event.preventDefault();
12828 // this is very hacky as keydown always get's upper case.
12830 var cc = String.fromCharCode(event.getCharCode());
12831 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12835 adjustWidth : function(tag, w){
12836 tag = tag.toLowerCase();
12837 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12838 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12839 if(tag == 'input'){
12842 if(tag == 'textarea'){
12845 }else if(Roo.isOpera){
12846 if(tag == 'input'){
12849 if(tag == 'textarea'){
12857 setFieldLabel : function(v)
12859 if(!this.rendered){
12863 if(this.indicatorEl()){
12864 var ar = this.el.select('label > span',true);
12866 if (ar.elements.length) {
12867 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12868 this.fieldLabel = v;
12872 var br = this.el.select('label',true);
12874 if(br.elements.length) {
12875 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12876 this.fieldLabel = v;
12880 Roo.log('Cannot Found any of label > span || label in input');
12884 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12885 this.fieldLabel = v;
12900 * @class Roo.bootstrap.TextArea
12901 * @extends Roo.bootstrap.Input
12902 * Bootstrap TextArea class
12903 * @cfg {Number} cols Specifies the visible width of a text area
12904 * @cfg {Number} rows Specifies the visible number of lines in a text area
12905 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12906 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12907 * @cfg {string} html text
12910 * Create a new TextArea
12911 * @param {Object} config The config object
12914 Roo.bootstrap.TextArea = function(config){
12915 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12919 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12929 getAutoCreate : function(){
12931 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12937 if(this.inputType != 'hidden'){
12938 cfg.cls = 'form-group' //input-group
12946 value : this.value || '',
12947 html: this.html || '',
12948 cls : 'form-control',
12949 placeholder : this.placeholder || ''
12953 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12954 input.maxLength = this.maxLength;
12958 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12962 input.cols = this.cols;
12965 if (this.readOnly) {
12966 input.readonly = true;
12970 input.name = this.name;
12974 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12978 ['xs','sm','md','lg'].map(function(size){
12979 if (settings[size]) {
12980 cfg.cls += ' col-' + size + '-' + settings[size];
12984 var inputblock = input;
12986 if(this.hasFeedback && !this.allowBlank){
12990 cls: 'glyphicon form-control-feedback'
12994 cls : 'has-feedback',
13003 if (this.before || this.after) {
13006 cls : 'input-group',
13010 inputblock.cn.push({
13012 cls : 'input-group-addon',
13017 inputblock.cn.push(input);
13019 if(this.hasFeedback && !this.allowBlank){
13020 inputblock.cls += ' has-feedback';
13021 inputblock.cn.push(feedback);
13025 inputblock.cn.push({
13027 cls : 'input-group-addon',
13034 if (align ==='left' && this.fieldLabel.length) {
13039 cls : 'control-label',
13040 html : this.fieldLabel
13051 if(this.labelWidth > 12){
13052 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13055 if(this.labelWidth < 13 && this.labelmd == 0){
13056 this.labelmd = this.labelWidth;
13059 if(this.labellg > 0){
13060 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13061 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13064 if(this.labelmd > 0){
13065 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13066 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13069 if(this.labelsm > 0){
13070 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13071 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13074 if(this.labelxs > 0){
13075 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13076 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13079 } else if ( this.fieldLabel.length) {
13084 //cls : 'input-group-addon',
13085 html : this.fieldLabel
13103 if (this.disabled) {
13104 input.disabled=true;
13111 * return the real textarea element.
13113 inputEl: function ()
13115 return this.el.select('textarea.form-control',true).first();
13119 * Clear any invalid styles/messages for this field
13121 clearInvalid : function()
13124 if(!this.el || this.preventMark){ // not rendered
13128 var label = this.el.select('label', true).first();
13129 var icon = this.el.select('i.fa-star', true).first();
13134 this.el.removeClass( this.validClass);
13135 this.inputEl().removeClass('is-invalid');
13137 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13139 var feedback = this.el.select('.form-control-feedback', true).first();
13142 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13147 this.fireEvent('valid', this);
13151 * Mark this field as valid
13153 markValid : function()
13155 if(!this.el || this.preventMark){ // not rendered
13159 this.el.removeClass([this.invalidClass, this.validClass]);
13160 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13162 var feedback = this.el.select('.form-control-feedback', true).first();
13165 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13168 if(this.disabled || this.allowBlank){
13172 var label = this.el.select('label', true).first();
13173 var icon = this.el.select('i.fa-star', true).first();
13178 if (Roo.bootstrap.version == 3) {
13179 this.el.addClass(this.validClass);
13181 this.inputEl().addClass('is-valid');
13185 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13187 var feedback = this.el.select('.form-control-feedback', true).first();
13190 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13191 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13196 this.fireEvent('valid', this);
13200 * Mark this field as invalid
13201 * @param {String} msg The validation message
13203 markInvalid : function(msg)
13205 if(!this.el || this.preventMark){ // not rendered
13209 this.el.removeClass([this.invalidClass, this.validClass]);
13210 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13212 var feedback = this.el.select('.form-control-feedback', true).first();
13215 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13218 if(this.disabled || this.allowBlank){
13222 var label = this.el.select('label', true).first();
13223 var icon = this.el.select('i.fa-star', true).first();
13225 if(!this.getValue().length && label && !icon){
13226 this.el.createChild({
13228 cls : 'text-danger fa fa-lg fa-star',
13229 tooltip : 'This field is required',
13230 style : 'margin-right:5px;'
13234 if (Roo.bootstrap.version == 3) {
13235 this.el.addClass(this.invalidClass);
13237 this.inputEl().addClass('is-invalid');
13240 // fixme ... this may be depricated need to test..
13241 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13243 var feedback = this.el.select('.form-control-feedback', true).first();
13246 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13248 if(this.getValue().length || this.forceFeedback){
13249 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13256 this.fireEvent('invalid', this, msg);
13264 * trigger field - base class for combo..
13269 * @class Roo.bootstrap.TriggerField
13270 * @extends Roo.bootstrap.Input
13271 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13272 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13273 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13274 * for which you can provide a custom implementation. For example:
13276 var trigger = new Roo.bootstrap.TriggerField();
13277 trigger.onTriggerClick = myTriggerFn;
13278 trigger.applyTo('my-field');
13281 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13282 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13283 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13284 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13285 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13288 * Create a new TriggerField.
13289 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13290 * to the base TextField)
13292 Roo.bootstrap.TriggerField = function(config){
13293 this.mimicing = false;
13294 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13297 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13299 * @cfg {String} triggerClass A CSS class to apply to the trigger
13302 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13307 * @cfg {Boolean} removable (true|false) special filter default false
13311 /** @cfg {Boolean} grow @hide */
13312 /** @cfg {Number} growMin @hide */
13313 /** @cfg {Number} growMax @hide */
13319 autoSize: Roo.emptyFn,
13323 deferHeight : true,
13326 actionMode : 'wrap',
13331 getAutoCreate : function(){
13333 var align = this.labelAlign || this.parentLabelAlign();
13338 cls: 'form-group' //input-group
13345 type : this.inputType,
13346 cls : 'form-control',
13347 autocomplete: 'new-password',
13348 placeholder : this.placeholder || ''
13352 input.name = this.name;
13355 input.cls += ' input-' + this.size;
13358 if (this.disabled) {
13359 input.disabled=true;
13362 var inputblock = input;
13364 if(this.hasFeedback && !this.allowBlank){
13368 cls: 'glyphicon form-control-feedback'
13371 if(this.removable && !this.editable ){
13373 cls : 'has-feedback',
13379 cls : 'roo-combo-removable-btn close'
13386 cls : 'has-feedback',
13395 if(this.removable && !this.editable ){
13397 cls : 'roo-removable',
13403 cls : 'roo-combo-removable-btn close'
13410 if (this.before || this.after) {
13413 cls : 'input-group',
13417 inputblock.cn.push({
13419 cls : 'input-group-addon input-group-prepend input-group-text',
13424 inputblock.cn.push(input);
13426 if(this.hasFeedback && !this.allowBlank){
13427 inputblock.cls += ' has-feedback';
13428 inputblock.cn.push(feedback);
13432 inputblock.cn.push({
13434 cls : 'input-group-addon input-group-append input-group-text',
13443 var ibwrap = inputblock;
13448 cls: 'roo-select2-choices',
13452 cls: 'roo-select2-search-field',
13464 cls: 'roo-select2-container input-group',
13469 cls: 'form-hidden-field'
13475 if(!this.multiple && this.showToggleBtn){
13481 if (this.caret != false) {
13484 cls: 'fa fa-' + this.caret
13491 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13493 Roo.bootstrap.version == 3 ? caret : '',
13496 cls: 'combobox-clear',
13510 combobox.cls += ' roo-select2-container-multi';
13514 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13515 tooltip : 'This field is required'
13517 if (Roo.bootstrap.version == 4) {
13520 style : 'display:none'
13525 if (align ==='left' && this.fieldLabel.length) {
13527 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13534 cls : 'control-label',
13535 html : this.fieldLabel
13547 var labelCfg = cfg.cn[1];
13548 var contentCfg = cfg.cn[2];
13550 if(this.indicatorpos == 'right'){
13555 cls : 'control-label',
13559 html : this.fieldLabel
13573 labelCfg = cfg.cn[0];
13574 contentCfg = cfg.cn[1];
13577 if(this.labelWidth > 12){
13578 labelCfg.style = "width: " + this.labelWidth + 'px';
13581 if(this.labelWidth < 13 && this.labelmd == 0){
13582 this.labelmd = this.labelWidth;
13585 if(this.labellg > 0){
13586 labelCfg.cls += ' col-lg-' + this.labellg;
13587 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13590 if(this.labelmd > 0){
13591 labelCfg.cls += ' col-md-' + this.labelmd;
13592 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13595 if(this.labelsm > 0){
13596 labelCfg.cls += ' col-sm-' + this.labelsm;
13597 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13600 if(this.labelxs > 0){
13601 labelCfg.cls += ' col-xs-' + this.labelxs;
13602 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13605 } else if ( this.fieldLabel.length) {
13606 // Roo.log(" label");
13611 //cls : 'input-group-addon',
13612 html : this.fieldLabel
13620 if(this.indicatorpos == 'right'){
13628 html : this.fieldLabel
13642 // Roo.log(" no label && no align");
13649 ['xs','sm','md','lg'].map(function(size){
13650 if (settings[size]) {
13651 cfg.cls += ' col-' + size + '-' + settings[size];
13662 onResize : function(w, h){
13663 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13664 // if(typeof w == 'number'){
13665 // var x = w - this.trigger.getWidth();
13666 // this.inputEl().setWidth(this.adjustWidth('input', x));
13667 // this.trigger.setStyle('left', x+'px');
13672 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13675 getResizeEl : function(){
13676 return this.inputEl();
13680 getPositionEl : function(){
13681 return this.inputEl();
13685 alignErrorIcon : function(){
13686 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13690 initEvents : function(){
13694 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13695 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13696 if(!this.multiple && this.showToggleBtn){
13697 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13698 if(this.hideTrigger){
13699 this.trigger.setDisplayed(false);
13701 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13705 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13708 if(this.removable && !this.editable && !this.tickable){
13709 var close = this.closeTriggerEl();
13712 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13713 close.on('click', this.removeBtnClick, this, close);
13717 //this.trigger.addClassOnOver('x-form-trigger-over');
13718 //this.trigger.addClassOnClick('x-form-trigger-click');
13721 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13725 closeTriggerEl : function()
13727 var close = this.el.select('.roo-combo-removable-btn', true).first();
13728 return close ? close : false;
13731 removeBtnClick : function(e, h, el)
13733 e.preventDefault();
13735 if(this.fireEvent("remove", this) !== false){
13737 this.fireEvent("afterremove", this)
13741 createList : function()
13743 this.list = Roo.get(document.body).createChild({
13744 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13745 cls: 'typeahead typeahead-long dropdown-menu shadow',
13746 style: 'display:none'
13749 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13754 initTrigger : function(){
13759 onDestroy : function(){
13761 this.trigger.removeAllListeners();
13762 // this.trigger.remove();
13765 // this.wrap.remove();
13767 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13771 onFocus : function(){
13772 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13774 if(!this.mimicing){
13775 this.wrap.addClass('x-trigger-wrap-focus');
13776 this.mimicing = true;
13777 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13778 if(this.monitorTab){
13779 this.el.on("keydown", this.checkTab, this);
13786 checkTab : function(e){
13787 if(e.getKey() == e.TAB){
13788 this.triggerBlur();
13793 onBlur : function(){
13798 mimicBlur : function(e, t){
13800 if(!this.wrap.contains(t) && this.validateBlur()){
13801 this.triggerBlur();
13807 triggerBlur : function(){
13808 this.mimicing = false;
13809 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13810 if(this.monitorTab){
13811 this.el.un("keydown", this.checkTab, this);
13813 //this.wrap.removeClass('x-trigger-wrap-focus');
13814 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13818 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13819 validateBlur : function(e, t){
13824 onDisable : function(){
13825 this.inputEl().dom.disabled = true;
13826 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13828 // this.wrap.addClass('x-item-disabled');
13833 onEnable : function(){
13834 this.inputEl().dom.disabled = false;
13835 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13837 // this.el.removeClass('x-item-disabled');
13842 onShow : function(){
13843 var ae = this.getActionEl();
13846 ae.dom.style.display = '';
13847 ae.dom.style.visibility = 'visible';
13853 onHide : function(){
13854 var ae = this.getActionEl();
13855 ae.dom.style.display = 'none';
13859 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13860 * by an implementing function.
13862 * @param {EventObject} e
13864 onTriggerClick : Roo.emptyFn
13872 * @class Roo.bootstrap.CardUploader
13873 * @extends Roo.bootstrap.Button
13874 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13875 * @cfg {Number} errorTimeout default 3000
13876 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13877 * @cfg {Array} html The button text.
13881 * Create a new CardUploader
13882 * @param {Object} config The config object
13885 Roo.bootstrap.CardUploader = function(config){
13889 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13892 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13900 * When a image is clicked on - and needs to display a slideshow or similar..
13901 * @param {Roo.bootstrap.Card} this
13902 * @param {Object} The image information data
13908 * When a the download link is clicked
13909 * @param {Roo.bootstrap.Card} this
13910 * @param {Object} The image information data contains
13917 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13920 errorTimeout : 3000,
13924 fileCollection : false,
13927 getAutoCreate : function()
13931 cls :'form-group' ,
13936 //cls : 'input-group-addon',
13937 html : this.fieldLabel
13945 value : this.value,
13946 cls : 'd-none form-control'
13951 multiple : 'multiple',
13953 cls : 'd-none roo-card-upload-selector'
13957 cls : 'roo-card-uploader-button-container w-100 mb-2'
13960 cls : 'card-columns roo-card-uploader-container'
13970 getChildContainer : function() /// what children are added to.
13972 return this.containerEl;
13975 getButtonContainer : function() /// what children are added to.
13977 return this.el.select(".roo-card-uploader-button-container").first();
13980 initEvents : function()
13983 Roo.bootstrap.Input.prototype.initEvents.call(this);
13987 xns: Roo.bootstrap,
13990 container_method : 'getButtonContainer' ,
13991 html : this.html, // fix changable?
13994 'click' : function(btn, e) {
14003 this.urlAPI = (window.createObjectURL && window) ||
14004 (window.URL && URL.revokeObjectURL && URL) ||
14005 (window.webkitURL && webkitURL);
14010 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14012 this.selectorEl.on('change', this.onFileSelected, this);
14015 this.images.forEach(function(img) {
14018 this.images = false;
14020 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14026 onClick : function(e)
14028 e.preventDefault();
14030 this.selectorEl.dom.click();
14034 onFileSelected : function(e)
14036 e.preventDefault();
14038 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14042 Roo.each(this.selectorEl.dom.files, function(file){
14043 this.addFile(file);
14052 addFile : function(file)
14055 if(typeof(file) === 'string'){
14056 throw "Add file by name?"; // should not happen
14060 if(!file || !this.urlAPI){
14070 var url = _this.urlAPI.createObjectURL( file);
14073 id : Roo.bootstrap.CardUploader.ID--,
14074 is_uploaded : false,
14078 mimetype : file.type,
14086 * addCard - add an Attachment to the uploader
14087 * @param data - the data about the image to upload
14091 title : "Title of file",
14092 is_uploaded : false,
14093 src : "http://.....",
14094 srcfile : { the File upload object },
14095 mimetype : file.type,
14098 .. any other data...
14104 addCard : function (data)
14106 // hidden input element?
14107 // if the file is not an image...
14108 //then we need to use something other that and header_image
14113 xns : Roo.bootstrap,
14114 xtype : 'CardFooter',
14117 xns : Roo.bootstrap,
14123 xns : Roo.bootstrap,
14125 html : String.format("<small>{0}</small>", data.title),
14126 cls : 'col-10 text-left',
14131 click : function() {
14133 t.fireEvent( "download", t, data );
14139 xns : Roo.bootstrap,
14141 style: 'max-height: 28px; ',
14147 click : function() {
14148 t.removeCard(data.id)
14160 var cn = this.addxtype(
14163 xns : Roo.bootstrap,
14166 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14167 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14168 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14173 initEvents : function() {
14174 Roo.bootstrap.Card.prototype.initEvents.call(this);
14176 this.imgEl = this.el.select('.card-img-top').first();
14178 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14179 this.imgEl.set({ 'pointer' : 'cursor' });
14182 this.getCardFooter().addClass('p-1');
14189 // dont' really need ot update items.
14190 // this.items.push(cn);
14191 this.fileCollection.add(cn);
14193 if (!data.srcfile) {
14194 this.updateInput();
14199 var reader = new FileReader();
14200 reader.addEventListener("load", function() {
14201 data.srcdata = reader.result;
14204 reader.readAsDataURL(data.srcfile);
14209 removeCard : function(id)
14212 var card = this.fileCollection.get(id);
14213 card.data.is_deleted = 1;
14214 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14215 //this.fileCollection.remove(card);
14216 //this.items = this.items.filter(function(e) { return e != card });
14217 // dont' really need ot update items.
14218 card.el.dom.parentNode.removeChild(card.el.dom);
14219 this.updateInput();
14225 this.fileCollection.each(function(card) {
14226 if (card.el.dom && card.el.dom.parentNode) {
14227 card.el.dom.parentNode.removeChild(card.el.dom);
14230 this.fileCollection.clear();
14231 this.updateInput();
14234 updateInput : function()
14237 this.fileCollection.each(function(e) {
14241 this.inputEl().dom.value = JSON.stringify(data);
14251 Roo.bootstrap.CardUploader.ID = -1;/*
14253 * Ext JS Library 1.1.1
14254 * Copyright(c) 2006-2007, Ext JS, LLC.
14256 * Originally Released Under LGPL - original licence link has changed is not relivant.
14259 * <script type="text/javascript">
14264 * @class Roo.data.SortTypes
14266 * Defines the default sorting (casting?) comparison functions used when sorting data.
14268 Roo.data.SortTypes = {
14270 * Default sort that does nothing
14271 * @param {Mixed} s The value being converted
14272 * @return {Mixed} The comparison value
14274 none : function(s){
14279 * The regular expression used to strip tags
14283 stripTagsRE : /<\/?[^>]+>/gi,
14286 * Strips all HTML tags to sort on text only
14287 * @param {Mixed} s The value being converted
14288 * @return {String} The comparison value
14290 asText : function(s){
14291 return String(s).replace(this.stripTagsRE, "");
14295 * Strips all HTML tags to sort on text only - Case insensitive
14296 * @param {Mixed} s The value being converted
14297 * @return {String} The comparison value
14299 asUCText : function(s){
14300 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14304 * Case insensitive string
14305 * @param {Mixed} s The value being converted
14306 * @return {String} The comparison value
14308 asUCString : function(s) {
14309 return String(s).toUpperCase();
14314 * @param {Mixed} s The value being converted
14315 * @return {Number} The comparison value
14317 asDate : function(s) {
14321 if(s instanceof Date){
14322 return s.getTime();
14324 return Date.parse(String(s));
14329 * @param {Mixed} s The value being converted
14330 * @return {Float} The comparison value
14332 asFloat : function(s) {
14333 var val = parseFloat(String(s).replace(/,/g, ""));
14342 * @param {Mixed} s The value being converted
14343 * @return {Number} The comparison value
14345 asInt : function(s) {
14346 var val = parseInt(String(s).replace(/,/g, ""));
14354 * Ext JS Library 1.1.1
14355 * Copyright(c) 2006-2007, Ext JS, LLC.
14357 * Originally Released Under LGPL - original licence link has changed is not relivant.
14360 * <script type="text/javascript">
14364 * @class Roo.data.Record
14365 * Instances of this class encapsulate both record <em>definition</em> information, and record
14366 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14367 * to access Records cached in an {@link Roo.data.Store} object.<br>
14369 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14370 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14373 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14375 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14376 * {@link #create}. The parameters are the same.
14377 * @param {Array} data An associative Array of data values keyed by the field name.
14378 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14379 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14380 * not specified an integer id is generated.
14382 Roo.data.Record = function(data, id){
14383 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14388 * Generate a constructor for a specific record layout.
14389 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14390 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14391 * Each field definition object may contain the following properties: <ul>
14392 * <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,
14393 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14394 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14395 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14396 * is being used, then this is a string containing the javascript expression to reference the data relative to
14397 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14398 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14399 * this may be omitted.</p></li>
14400 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14401 * <ul><li>auto (Default, implies no conversion)</li>
14406 * <li>date</li></ul></p></li>
14407 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14408 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14409 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14410 * by the Reader into an object that will be stored in the Record. It is passed the
14411 * following parameters:<ul>
14412 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14414 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14416 * <br>usage:<br><pre><code>
14417 var TopicRecord = Roo.data.Record.create(
14418 {name: 'title', mapping: 'topic_title'},
14419 {name: 'author', mapping: 'username'},
14420 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14421 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14422 {name: 'lastPoster', mapping: 'user2'},
14423 {name: 'excerpt', mapping: 'post_text'}
14426 var myNewRecord = new TopicRecord({
14427 title: 'Do my job please',
14430 lastPost: new Date(),
14431 lastPoster: 'Animal',
14432 excerpt: 'No way dude!'
14434 myStore.add(myNewRecord);
14439 Roo.data.Record.create = function(o){
14440 var f = function(){
14441 f.superclass.constructor.apply(this, arguments);
14443 Roo.extend(f, Roo.data.Record);
14444 var p = f.prototype;
14445 p.fields = new Roo.util.MixedCollection(false, function(field){
14448 for(var i = 0, len = o.length; i < len; i++){
14449 p.fields.add(new Roo.data.Field(o[i]));
14451 f.getField = function(name){
14452 return p.fields.get(name);
14457 Roo.data.Record.AUTO_ID = 1000;
14458 Roo.data.Record.EDIT = 'edit';
14459 Roo.data.Record.REJECT = 'reject';
14460 Roo.data.Record.COMMIT = 'commit';
14462 Roo.data.Record.prototype = {
14464 * Readonly flag - true if this record has been modified.
14473 join : function(store){
14474 this.store = store;
14478 * Set the named field to the specified value.
14479 * @param {String} name The name of the field to set.
14480 * @param {Object} value The value to set the field to.
14482 set : function(name, value){
14483 if(this.data[name] == value){
14487 if(!this.modified){
14488 this.modified = {};
14490 if(typeof this.modified[name] == 'undefined'){
14491 this.modified[name] = this.data[name];
14493 this.data[name] = value;
14494 if(!this.editing && this.store){
14495 this.store.afterEdit(this);
14500 * Get the value of the named field.
14501 * @param {String} name The name of the field to get the value of.
14502 * @return {Object} The value of the field.
14504 get : function(name){
14505 return this.data[name];
14509 beginEdit : function(){
14510 this.editing = true;
14511 this.modified = {};
14515 cancelEdit : function(){
14516 this.editing = false;
14517 delete this.modified;
14521 endEdit : function(){
14522 this.editing = false;
14523 if(this.dirty && this.store){
14524 this.store.afterEdit(this);
14529 * Usually called by the {@link Roo.data.Store} which owns the Record.
14530 * Rejects all changes made to the Record since either creation, or the last commit operation.
14531 * Modified fields are reverted to their original values.
14533 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14534 * of reject operations.
14536 reject : function(){
14537 var m = this.modified;
14539 if(typeof m[n] != "function"){
14540 this.data[n] = m[n];
14543 this.dirty = false;
14544 delete this.modified;
14545 this.editing = false;
14547 this.store.afterReject(this);
14552 * Usually called by the {@link Roo.data.Store} which owns the Record.
14553 * Commits all changes made to the Record since either creation, or the last commit operation.
14555 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14556 * of commit operations.
14558 commit : function(){
14559 this.dirty = false;
14560 delete this.modified;
14561 this.editing = false;
14563 this.store.afterCommit(this);
14568 hasError : function(){
14569 return this.error != null;
14573 clearError : function(){
14578 * Creates a copy of this record.
14579 * @param {String} id (optional) A new record id if you don't want to use this record's id
14582 copy : function(newId) {
14583 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14587 * Ext JS Library 1.1.1
14588 * Copyright(c) 2006-2007, Ext JS, LLC.
14590 * Originally Released Under LGPL - original licence link has changed is not relivant.
14593 * <script type="text/javascript">
14599 * @class Roo.data.Store
14600 * @extends Roo.util.Observable
14601 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14602 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14604 * 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
14605 * has no knowledge of the format of the data returned by the Proxy.<br>
14607 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14608 * instances from the data object. These records are cached and made available through accessor functions.
14610 * Creates a new Store.
14611 * @param {Object} config A config object containing the objects needed for the Store to access data,
14612 * and read the data into Records.
14614 Roo.data.Store = function(config){
14615 this.data = new Roo.util.MixedCollection(false);
14616 this.data.getKey = function(o){
14619 this.baseParams = {};
14621 this.paramNames = {
14626 "multisort" : "_multisort"
14629 if(config && config.data){
14630 this.inlineData = config.data;
14631 delete config.data;
14634 Roo.apply(this, config);
14636 if(this.reader){ // reader passed
14637 this.reader = Roo.factory(this.reader, Roo.data);
14638 this.reader.xmodule = this.xmodule || false;
14639 if(!this.recordType){
14640 this.recordType = this.reader.recordType;
14642 if(this.reader.onMetaChange){
14643 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14647 if(this.recordType){
14648 this.fields = this.recordType.prototype.fields;
14650 this.modified = [];
14654 * @event datachanged
14655 * Fires when the data cache has changed, and a widget which is using this Store
14656 * as a Record cache should refresh its view.
14657 * @param {Store} this
14659 datachanged : true,
14661 * @event metachange
14662 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14663 * @param {Store} this
14664 * @param {Object} meta The JSON metadata
14669 * Fires when Records have been added to the Store
14670 * @param {Store} this
14671 * @param {Roo.data.Record[]} records The array of Records added
14672 * @param {Number} index The index at which the record(s) were added
14677 * Fires when a Record has been removed from the Store
14678 * @param {Store} this
14679 * @param {Roo.data.Record} record The Record that was removed
14680 * @param {Number} index The index at which the record was removed
14685 * Fires when a Record has been updated
14686 * @param {Store} this
14687 * @param {Roo.data.Record} record The Record that was updated
14688 * @param {String} operation The update operation being performed. Value may be one of:
14690 Roo.data.Record.EDIT
14691 Roo.data.Record.REJECT
14692 Roo.data.Record.COMMIT
14698 * Fires when the data cache has been cleared.
14699 * @param {Store} this
14703 * @event beforeload
14704 * Fires before a request is made for a new data object. If the beforeload handler returns false
14705 * the load action will be canceled.
14706 * @param {Store} this
14707 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14711 * @event beforeloadadd
14712 * Fires after a new set of Records has been loaded.
14713 * @param {Store} this
14714 * @param {Roo.data.Record[]} records The Records that were loaded
14715 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14717 beforeloadadd : true,
14720 * Fires after a new set of Records has been loaded, before they are added to the store.
14721 * @param {Store} this
14722 * @param {Roo.data.Record[]} records The Records that were loaded
14723 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14724 * @params {Object} return from reader
14728 * @event loadexception
14729 * Fires if an exception occurs in the Proxy during loading.
14730 * Called with the signature of the Proxy's "loadexception" event.
14731 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14734 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14735 * @param {Object} load options
14736 * @param {Object} jsonData from your request (normally this contains the Exception)
14738 loadexception : true
14742 this.proxy = Roo.factory(this.proxy, Roo.data);
14743 this.proxy.xmodule = this.xmodule || false;
14744 this.relayEvents(this.proxy, ["loadexception"]);
14746 this.sortToggle = {};
14747 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14749 Roo.data.Store.superclass.constructor.call(this);
14751 if(this.inlineData){
14752 this.loadData(this.inlineData);
14753 delete this.inlineData;
14757 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14759 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14760 * without a remote query - used by combo/forms at present.
14764 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14767 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14770 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
14771 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14774 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14775 * on any HTTP request
14778 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14781 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14785 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14786 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14788 remoteSort : false,
14791 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14792 * loaded or when a record is removed. (defaults to false).
14794 pruneModifiedRecords : false,
14797 lastOptions : null,
14800 * Add Records to the Store and fires the add event.
14801 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14803 add : function(records){
14804 records = [].concat(records);
14805 for(var i = 0, len = records.length; i < len; i++){
14806 records[i].join(this);
14808 var index = this.data.length;
14809 this.data.addAll(records);
14810 this.fireEvent("add", this, records, index);
14814 * Remove a Record from the Store and fires the remove event.
14815 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14817 remove : function(record){
14818 var index = this.data.indexOf(record);
14819 this.data.removeAt(index);
14821 if(this.pruneModifiedRecords){
14822 this.modified.remove(record);
14824 this.fireEvent("remove", this, record, index);
14828 * Remove all Records from the Store and fires the clear event.
14830 removeAll : function(){
14832 if(this.pruneModifiedRecords){
14833 this.modified = [];
14835 this.fireEvent("clear", this);
14839 * Inserts Records to the Store at the given index and fires the add event.
14840 * @param {Number} index The start index at which to insert the passed Records.
14841 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14843 insert : function(index, records){
14844 records = [].concat(records);
14845 for(var i = 0, len = records.length; i < len; i++){
14846 this.data.insert(index, records[i]);
14847 records[i].join(this);
14849 this.fireEvent("add", this, records, index);
14853 * Get the index within the cache of the passed Record.
14854 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14855 * @return {Number} The index of the passed Record. Returns -1 if not found.
14857 indexOf : function(record){
14858 return this.data.indexOf(record);
14862 * Get the index within the cache of the Record with the passed id.
14863 * @param {String} id The id of the Record to find.
14864 * @return {Number} The index of the Record. Returns -1 if not found.
14866 indexOfId : function(id){
14867 return this.data.indexOfKey(id);
14871 * Get the Record with the specified id.
14872 * @param {String} id The id of the Record to find.
14873 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14875 getById : function(id){
14876 return this.data.key(id);
14880 * Get the Record at the specified index.
14881 * @param {Number} index The index of the Record to find.
14882 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14884 getAt : function(index){
14885 return this.data.itemAt(index);
14889 * Returns a range of Records between specified indices.
14890 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14891 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14892 * @return {Roo.data.Record[]} An array of Records
14894 getRange : function(start, end){
14895 return this.data.getRange(start, end);
14899 storeOptions : function(o){
14900 o = Roo.apply({}, o);
14903 this.lastOptions = o;
14907 * Loads the Record cache from the configured Proxy using the configured Reader.
14909 * If using remote paging, then the first load call must specify the <em>start</em>
14910 * and <em>limit</em> properties in the options.params property to establish the initial
14911 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14913 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14914 * and this call will return before the new data has been loaded. Perform any post-processing
14915 * in a callback function, or in a "load" event handler.</strong>
14917 * @param {Object} options An object containing properties which control loading options:<ul>
14918 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14919 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14920 * passed the following arguments:<ul>
14921 * <li>r : Roo.data.Record[]</li>
14922 * <li>options: Options object from the load call</li>
14923 * <li>success: Boolean success indicator</li></ul></li>
14924 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14925 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14928 load : function(options){
14929 options = options || {};
14930 if(this.fireEvent("beforeload", this, options) !== false){
14931 this.storeOptions(options);
14932 var p = Roo.apply(options.params || {}, this.baseParams);
14933 // if meta was not loaded from remote source.. try requesting it.
14934 if (!this.reader.metaFromRemote) {
14935 p._requestMeta = 1;
14937 if(this.sortInfo && this.remoteSort){
14938 var pn = this.paramNames;
14939 p[pn["sort"]] = this.sortInfo.field;
14940 p[pn["dir"]] = this.sortInfo.direction;
14942 if (this.multiSort) {
14943 var pn = this.paramNames;
14944 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14947 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14952 * Reloads the Record cache from the configured Proxy using the configured Reader and
14953 * the options from the last load operation performed.
14954 * @param {Object} options (optional) An object containing properties which may override the options
14955 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14956 * the most recently used options are reused).
14958 reload : function(options){
14959 this.load(Roo.applyIf(options||{}, this.lastOptions));
14963 // Called as a callback by the Reader during a load operation.
14964 loadRecords : function(o, options, success){
14965 if(!o || success === false){
14966 if(success !== false){
14967 this.fireEvent("load", this, [], options, o);
14969 if(options.callback){
14970 options.callback.call(options.scope || this, [], options, false);
14974 // if data returned failure - throw an exception.
14975 if (o.success === false) {
14976 // show a message if no listener is registered.
14977 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14978 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14980 // loadmask wil be hooked into this..
14981 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14984 var r = o.records, t = o.totalRecords || r.length;
14986 this.fireEvent("beforeloadadd", this, r, options, o);
14988 if(!options || options.add !== true){
14989 if(this.pruneModifiedRecords){
14990 this.modified = [];
14992 for(var i = 0, len = r.length; i < len; i++){
14996 this.data = this.snapshot;
14997 delete this.snapshot;
15000 this.data.addAll(r);
15001 this.totalLength = t;
15003 this.fireEvent("datachanged", this);
15005 this.totalLength = Math.max(t, this.data.length+r.length);
15009 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15011 var e = new Roo.data.Record({});
15013 e.set(this.parent.displayField, this.parent.emptyTitle);
15014 e.set(this.parent.valueField, '');
15019 this.fireEvent("load", this, r, options, o);
15020 if(options.callback){
15021 options.callback.call(options.scope || this, r, options, true);
15027 * Loads data from a passed data block. A Reader which understands the format of the data
15028 * must have been configured in the constructor.
15029 * @param {Object} data The data block from which to read the Records. The format of the data expected
15030 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15031 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15033 loadData : function(o, append){
15034 var r = this.reader.readRecords(o);
15035 this.loadRecords(r, {add: append}, true);
15039 * using 'cn' the nested child reader read the child array into it's child stores.
15040 * @param {Object} rec The record with a 'children array
15042 loadDataFromChildren : function(rec)
15044 this.loadData(this.reader.toLoadData(rec));
15049 * Gets the number of cached records.
15051 * <em>If using paging, this may not be the total size of the dataset. If the data object
15052 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15053 * the data set size</em>
15055 getCount : function(){
15056 return this.data.length || 0;
15060 * Gets the total number of records in the dataset as returned by the server.
15062 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15063 * the dataset size</em>
15065 getTotalCount : function(){
15066 return this.totalLength || 0;
15070 * Returns the sort state of the Store as an object with two properties:
15072 field {String} The name of the field by which the Records are sorted
15073 direction {String} The sort order, "ASC" or "DESC"
15076 getSortState : function(){
15077 return this.sortInfo;
15081 applySort : function(){
15082 if(this.sortInfo && !this.remoteSort){
15083 var s = this.sortInfo, f = s.field;
15084 var st = this.fields.get(f).sortType;
15085 var fn = function(r1, r2){
15086 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15087 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15089 this.data.sort(s.direction, fn);
15090 if(this.snapshot && this.snapshot != this.data){
15091 this.snapshot.sort(s.direction, fn);
15097 * Sets the default sort column and order to be used by the next load operation.
15098 * @param {String} fieldName The name of the field to sort by.
15099 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15101 setDefaultSort : function(field, dir){
15102 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15106 * Sort the Records.
15107 * If remote sorting is used, the sort is performed on the server, and the cache is
15108 * reloaded. If local sorting is used, the cache is sorted internally.
15109 * @param {String} fieldName The name of the field to sort by.
15110 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15112 sort : function(fieldName, dir){
15113 var f = this.fields.get(fieldName);
15115 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15117 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15118 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15123 this.sortToggle[f.name] = dir;
15124 this.sortInfo = {field: f.name, direction: dir};
15125 if(!this.remoteSort){
15127 this.fireEvent("datachanged", this);
15129 this.load(this.lastOptions);
15134 * Calls the specified function for each of the Records in the cache.
15135 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15136 * Returning <em>false</em> aborts and exits the iteration.
15137 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15139 each : function(fn, scope){
15140 this.data.each(fn, scope);
15144 * Gets all records modified since the last commit. Modified records are persisted across load operations
15145 * (e.g., during paging).
15146 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15148 getModifiedRecords : function(){
15149 return this.modified;
15153 createFilterFn : function(property, value, anyMatch){
15154 if(!value.exec){ // not a regex
15155 value = String(value);
15156 if(value.length == 0){
15159 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15161 return function(r){
15162 return value.test(r.data[property]);
15167 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15168 * @param {String} property A field on your records
15169 * @param {Number} start The record index to start at (defaults to 0)
15170 * @param {Number} end The last record index to include (defaults to length - 1)
15171 * @return {Number} The sum
15173 sum : function(property, start, end){
15174 var rs = this.data.items, v = 0;
15175 start = start || 0;
15176 end = (end || end === 0) ? end : rs.length-1;
15178 for(var i = start; i <= end; i++){
15179 v += (rs[i].data[property] || 0);
15185 * Filter the records by a specified property.
15186 * @param {String} field A field on your records
15187 * @param {String/RegExp} value Either a string that the field
15188 * should start with or a RegExp to test against the field
15189 * @param {Boolean} anyMatch True to match any part not just the beginning
15191 filter : function(property, value, anyMatch){
15192 var fn = this.createFilterFn(property, value, anyMatch);
15193 return fn ? this.filterBy(fn) : this.clearFilter();
15197 * Filter by a function. The specified function will be called with each
15198 * record in this data source. If the function returns true the record is included,
15199 * otherwise it is filtered.
15200 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15201 * @param {Object} scope (optional) The scope of the function (defaults to this)
15203 filterBy : function(fn, scope){
15204 this.snapshot = this.snapshot || this.data;
15205 this.data = this.queryBy(fn, scope||this);
15206 this.fireEvent("datachanged", this);
15210 * Query the records by a specified property.
15211 * @param {String} field A field on your records
15212 * @param {String/RegExp} value Either a string that the field
15213 * should start with or a RegExp to test against the field
15214 * @param {Boolean} anyMatch True to match any part not just the beginning
15215 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15217 query : function(property, value, anyMatch){
15218 var fn = this.createFilterFn(property, value, anyMatch);
15219 return fn ? this.queryBy(fn) : this.data.clone();
15223 * Query by a function. The specified function will be called with each
15224 * record in this data source. If the function returns true the record is included
15226 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15227 * @param {Object} scope (optional) The scope of the function (defaults to this)
15228 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15230 queryBy : function(fn, scope){
15231 var data = this.snapshot || this.data;
15232 return data.filterBy(fn, scope||this);
15236 * Collects unique values for a particular dataIndex from this store.
15237 * @param {String} dataIndex The property to collect
15238 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15239 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15240 * @return {Array} An array of the unique values
15242 collect : function(dataIndex, allowNull, bypassFilter){
15243 var d = (bypassFilter === true && this.snapshot) ?
15244 this.snapshot.items : this.data.items;
15245 var v, sv, r = [], l = {};
15246 for(var i = 0, len = d.length; i < len; i++){
15247 v = d[i].data[dataIndex];
15249 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15258 * Revert to a view of the Record cache with no filtering applied.
15259 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15261 clearFilter : function(suppressEvent){
15262 if(this.snapshot && this.snapshot != this.data){
15263 this.data = this.snapshot;
15264 delete this.snapshot;
15265 if(suppressEvent !== true){
15266 this.fireEvent("datachanged", this);
15272 afterEdit : function(record){
15273 if(this.modified.indexOf(record) == -1){
15274 this.modified.push(record);
15276 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15280 afterReject : function(record){
15281 this.modified.remove(record);
15282 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15286 afterCommit : function(record){
15287 this.modified.remove(record);
15288 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15292 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15293 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15295 commitChanges : function(){
15296 var m = this.modified.slice(0);
15297 this.modified = [];
15298 for(var i = 0, len = m.length; i < len; i++){
15304 * Cancel outstanding changes on all changed records.
15306 rejectChanges : function(){
15307 var m = this.modified.slice(0);
15308 this.modified = [];
15309 for(var i = 0, len = m.length; i < len; i++){
15314 onMetaChange : function(meta, rtype, o){
15315 this.recordType = rtype;
15316 this.fields = rtype.prototype.fields;
15317 delete this.snapshot;
15318 this.sortInfo = meta.sortInfo || this.sortInfo;
15319 this.modified = [];
15320 this.fireEvent('metachange', this, this.reader.meta);
15323 moveIndex : function(data, type)
15325 var index = this.indexOf(data);
15327 var newIndex = index + type;
15331 this.insert(newIndex, data);
15336 * Ext JS Library 1.1.1
15337 * Copyright(c) 2006-2007, Ext JS, LLC.
15339 * Originally Released Under LGPL - original licence link has changed is not relivant.
15342 * <script type="text/javascript">
15346 * @class Roo.data.SimpleStore
15347 * @extends Roo.data.Store
15348 * Small helper class to make creating Stores from Array data easier.
15349 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15350 * @cfg {Array} fields An array of field definition objects, or field name strings.
15351 * @cfg {Object} an existing reader (eg. copied from another store)
15352 * @cfg {Array} data The multi-dimensional array of data
15353 * @cfg {Roo.data.DataProxy} proxy [not-required]
15354 * @cfg {Roo.data.Reader} reader [not-required]
15356 * @param {Object} config
15358 Roo.data.SimpleStore = function(config)
15360 Roo.data.SimpleStore.superclass.constructor.call(this, {
15362 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15365 Roo.data.Record.create(config.fields)
15367 proxy : new Roo.data.MemoryProxy(config.data)
15371 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15373 * Ext JS Library 1.1.1
15374 * Copyright(c) 2006-2007, Ext JS, LLC.
15376 * Originally Released Under LGPL - original licence link has changed is not relivant.
15379 * <script type="text/javascript">
15384 * @extends Roo.data.Store
15385 * @class Roo.data.JsonStore
15386 * Small helper class to make creating Stores for JSON data easier. <br/>
15388 var store = new Roo.data.JsonStore({
15389 url: 'get-images.php',
15391 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15394 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15395 * JsonReader and HttpProxy (unless inline data is provided).</b>
15396 * @cfg {Array} fields An array of field definition objects, or field name strings.
15398 * @param {Object} config
15400 Roo.data.JsonStore = function(c){
15401 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15402 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15403 reader: new Roo.data.JsonReader(c, c.fields)
15406 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15408 * Ext JS Library 1.1.1
15409 * Copyright(c) 2006-2007, Ext JS, LLC.
15411 * Originally Released Under LGPL - original licence link has changed is not relivant.
15414 * <script type="text/javascript">
15418 Roo.data.Field = function(config){
15419 if(typeof config == "string"){
15420 config = {name: config};
15422 Roo.apply(this, config);
15425 this.type = "auto";
15428 var st = Roo.data.SortTypes;
15429 // named sortTypes are supported, here we look them up
15430 if(typeof this.sortType == "string"){
15431 this.sortType = st[this.sortType];
15434 // set default sortType for strings and dates
15435 if(!this.sortType){
15438 this.sortType = st.asUCString;
15441 this.sortType = st.asDate;
15444 this.sortType = st.none;
15449 var stripRe = /[\$,%]/g;
15451 // prebuilt conversion function for this field, instead of
15452 // switching every time we're reading a value
15454 var cv, dateFormat = this.dateFormat;
15459 cv = function(v){ return v; };
15462 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15466 return v !== undefined && v !== null && v !== '' ?
15467 parseInt(String(v).replace(stripRe, ""), 10) : '';
15472 return v !== undefined && v !== null && v !== '' ?
15473 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15478 cv = function(v){ return v === true || v === "true" || v == 1; };
15485 if(v instanceof Date){
15489 if(dateFormat == "timestamp"){
15490 return new Date(v*1000);
15492 return Date.parseDate(v, dateFormat);
15494 var parsed = Date.parse(v);
15495 return parsed ? new Date(parsed) : null;
15504 Roo.data.Field.prototype = {
15512 * Ext JS Library 1.1.1
15513 * Copyright(c) 2006-2007, Ext JS, LLC.
15515 * Originally Released Under LGPL - original licence link has changed is not relivant.
15518 * <script type="text/javascript">
15521 // Base class for reading structured data from a data source. This class is intended to be
15522 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15525 * @class Roo.data.DataReader
15527 * Base class for reading structured data from a data source. This class is intended to be
15528 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15531 Roo.data.DataReader = function(meta, recordType){
15535 this.recordType = recordType instanceof Array ?
15536 Roo.data.Record.create(recordType) : recordType;
15539 Roo.data.DataReader.prototype = {
15542 readerType : 'Data',
15544 * Create an empty record
15545 * @param {Object} data (optional) - overlay some values
15546 * @return {Roo.data.Record} record created.
15548 newRow : function(d) {
15550 this.recordType.prototype.fields.each(function(c) {
15552 case 'int' : da[c.name] = 0; break;
15553 case 'date' : da[c.name] = new Date(); break;
15554 case 'float' : da[c.name] = 0.0; break;
15555 case 'boolean' : da[c.name] = false; break;
15556 default : da[c.name] = ""; break;
15560 return new this.recordType(Roo.apply(da, d));
15566 * Ext JS Library 1.1.1
15567 * Copyright(c) 2006-2007, Ext JS, LLC.
15569 * Originally Released Under LGPL - original licence link has changed is not relivant.
15572 * <script type="text/javascript">
15576 * @class Roo.data.DataProxy
15577 * @extends Roo.data.Observable
15579 * This class is an abstract base class for implementations which provide retrieval of
15580 * unformatted data objects.<br>
15582 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15583 * (of the appropriate type which knows how to parse the data object) to provide a block of
15584 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15586 * Custom implementations must implement the load method as described in
15587 * {@link Roo.data.HttpProxy#load}.
15589 Roo.data.DataProxy = function(){
15592 * @event beforeload
15593 * Fires before a network request is made to retrieve a data object.
15594 * @param {Object} This DataProxy object.
15595 * @param {Object} params The params parameter to the load function.
15600 * Fires before the load method's callback is called.
15601 * @param {Object} This DataProxy object.
15602 * @param {Object} o The data object.
15603 * @param {Object} arg The callback argument object passed to the load function.
15607 * @event loadexception
15608 * Fires if an Exception occurs during data retrieval.
15609 * @param {Object} This DataProxy object.
15610 * @param {Object} o The data object.
15611 * @param {Object} arg The callback argument object passed to the load function.
15612 * @param {Object} e The Exception.
15614 loadexception : true
15616 Roo.data.DataProxy.superclass.constructor.call(this);
15619 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15622 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15626 * Ext JS Library 1.1.1
15627 * Copyright(c) 2006-2007, Ext JS, LLC.
15629 * Originally Released Under LGPL - original licence link has changed is not relivant.
15632 * <script type="text/javascript">
15635 * @class Roo.data.MemoryProxy
15636 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15637 * to the Reader when its load method is called.
15639 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15641 Roo.data.MemoryProxy = function(data){
15645 Roo.data.MemoryProxy.superclass.constructor.call(this);
15649 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15652 * Load data from the requested source (in this case an in-memory
15653 * data object passed to the constructor), read the data object into
15654 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15655 * process that block using the passed callback.
15656 * @param {Object} params This parameter is not used by the MemoryProxy class.
15657 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15658 * object into a block of Roo.data.Records.
15659 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15660 * The function must be passed <ul>
15661 * <li>The Record block object</li>
15662 * <li>The "arg" argument from the load function</li>
15663 * <li>A boolean success indicator</li>
15665 * @param {Object} scope The scope in which to call the callback
15666 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15668 load : function(params, reader, callback, scope, arg){
15669 params = params || {};
15672 result = reader.readRecords(params.data ? params.data :this.data);
15674 this.fireEvent("loadexception", this, arg, null, e);
15675 callback.call(scope, null, arg, false);
15678 callback.call(scope, result, arg, true);
15682 update : function(params, records){
15687 * Ext JS Library 1.1.1
15688 * Copyright(c) 2006-2007, Ext JS, LLC.
15690 * Originally Released Under LGPL - original licence link has changed is not relivant.
15693 * <script type="text/javascript">
15696 * @class Roo.data.HttpProxy
15697 * @extends Roo.data.DataProxy
15698 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15699 * configured to reference a certain URL.<br><br>
15701 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15702 * from which the running page was served.<br><br>
15704 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15706 * Be aware that to enable the browser to parse an XML document, the server must set
15707 * the Content-Type header in the HTTP response to "text/xml".
15709 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15710 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15711 * will be used to make the request.
15713 Roo.data.HttpProxy = function(conn){
15714 Roo.data.HttpProxy.superclass.constructor.call(this);
15715 // is conn a conn config or a real conn?
15717 this.useAjax = !conn || !conn.events;
15721 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15722 // thse are take from connection...
15725 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15728 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15729 * extra parameters to each request made by this object. (defaults to undefined)
15732 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15733 * to each request made by this object. (defaults to undefined)
15736 * @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)
15739 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15742 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15748 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15752 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15753 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15754 * a finer-grained basis than the DataProxy events.
15756 getConnection : function(){
15757 return this.useAjax ? Roo.Ajax : this.conn;
15761 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15762 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15763 * process that block using the passed callback.
15764 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15765 * for the request to the remote server.
15766 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15767 * object into a block of Roo.data.Records.
15768 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15769 * The function must be passed <ul>
15770 * <li>The Record block object</li>
15771 * <li>The "arg" argument from the load function</li>
15772 * <li>A boolean success indicator</li>
15774 * @param {Object} scope The scope in which to call the callback
15775 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15777 load : function(params, reader, callback, scope, arg){
15778 if(this.fireEvent("beforeload", this, params) !== false){
15780 params : params || {},
15782 callback : callback,
15787 callback : this.loadResponse,
15791 Roo.applyIf(o, this.conn);
15792 if(this.activeRequest){
15793 Roo.Ajax.abort(this.activeRequest);
15795 this.activeRequest = Roo.Ajax.request(o);
15797 this.conn.request(o);
15800 callback.call(scope||this, null, arg, false);
15805 loadResponse : function(o, success, response){
15806 delete this.activeRequest;
15808 this.fireEvent("loadexception", this, o, response);
15809 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15814 result = o.reader.read(response);
15816 this.fireEvent("loadexception", this, o, response, e);
15817 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15821 this.fireEvent("load", this, o, o.request.arg);
15822 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15826 update : function(dataSet){
15831 updateResponse : function(dataSet){
15836 * Ext JS Library 1.1.1
15837 * Copyright(c) 2006-2007, Ext JS, LLC.
15839 * Originally Released Under LGPL - original licence link has changed is not relivant.
15842 * <script type="text/javascript">
15846 * @class Roo.data.ScriptTagProxy
15847 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15848 * other than the originating domain of the running page.<br><br>
15850 * <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
15851 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15853 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15854 * source code that is used as the source inside a <script> tag.<br><br>
15856 * In order for the browser to process the returned data, the server must wrap the data object
15857 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15858 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15859 * depending on whether the callback name was passed:
15862 boolean scriptTag = false;
15863 String cb = request.getParameter("callback");
15866 response.setContentType("text/javascript");
15868 response.setContentType("application/x-json");
15870 Writer out = response.getWriter();
15872 out.write(cb + "(");
15874 out.print(dataBlock.toJsonString());
15881 * @param {Object} config A configuration object.
15883 Roo.data.ScriptTagProxy = function(config){
15884 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15885 Roo.apply(this, config);
15886 this.head = document.getElementsByTagName("head")[0];
15889 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15891 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15893 * @cfg {String} url The URL from which to request the data object.
15896 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15900 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15901 * the server the name of the callback function set up by the load call to process the returned data object.
15902 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15903 * javascript output which calls this named function passing the data object as its only parameter.
15905 callbackParam : "callback",
15907 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15908 * name to the request.
15913 * Load data from the configured URL, read the data object into
15914 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15915 * process that block using the passed callback.
15916 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15917 * for the request to the remote server.
15918 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15919 * object into a block of Roo.data.Records.
15920 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15921 * The function must be passed <ul>
15922 * <li>The Record block object</li>
15923 * <li>The "arg" argument from the load function</li>
15924 * <li>A boolean success indicator</li>
15926 * @param {Object} scope The scope in which to call the callback
15927 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15929 load : function(params, reader, callback, scope, arg){
15930 if(this.fireEvent("beforeload", this, params) !== false){
15932 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15934 var url = this.url;
15935 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15937 url += "&_dc=" + (new Date().getTime());
15939 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15942 cb : "stcCallback"+transId,
15943 scriptId : "stcScript"+transId,
15947 callback : callback,
15953 window[trans.cb] = function(o){
15954 conn.handleResponse(o, trans);
15957 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15959 if(this.autoAbort !== false){
15963 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15965 var script = document.createElement("script");
15966 script.setAttribute("src", url);
15967 script.setAttribute("type", "text/javascript");
15968 script.setAttribute("id", trans.scriptId);
15969 this.head.appendChild(script);
15971 this.trans = trans;
15973 callback.call(scope||this, null, arg, false);
15978 isLoading : function(){
15979 return this.trans ? true : false;
15983 * Abort the current server request.
15985 abort : function(){
15986 if(this.isLoading()){
15987 this.destroyTrans(this.trans);
15992 destroyTrans : function(trans, isLoaded){
15993 this.head.removeChild(document.getElementById(trans.scriptId));
15994 clearTimeout(trans.timeoutId);
15996 window[trans.cb] = undefined;
15998 delete window[trans.cb];
16001 // if hasn't been loaded, wait for load to remove it to prevent script error
16002 window[trans.cb] = function(){
16003 window[trans.cb] = undefined;
16005 delete window[trans.cb];
16012 handleResponse : function(o, trans){
16013 this.trans = false;
16014 this.destroyTrans(trans, true);
16017 result = trans.reader.readRecords(o);
16019 this.fireEvent("loadexception", this, o, trans.arg, e);
16020 trans.callback.call(trans.scope||window, null, trans.arg, false);
16023 this.fireEvent("load", this, o, trans.arg);
16024 trans.callback.call(trans.scope||window, result, trans.arg, true);
16028 handleFailure : function(trans){
16029 this.trans = false;
16030 this.destroyTrans(trans, false);
16031 this.fireEvent("loadexception", this, null, trans.arg);
16032 trans.callback.call(trans.scope||window, null, trans.arg, false);
16036 * Ext JS Library 1.1.1
16037 * Copyright(c) 2006-2007, Ext JS, LLC.
16039 * Originally Released Under LGPL - original licence link has changed is not relivant.
16042 * <script type="text/javascript">
16046 * @class Roo.data.JsonReader
16047 * @extends Roo.data.DataReader
16048 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16049 * based on mappings in a provided Roo.data.Record constructor.
16051 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16052 * in the reply previously.
16057 var RecordDef = Roo.data.Record.create([
16058 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16059 {name: 'occupation'} // This field will use "occupation" as the mapping.
16061 var myReader = new Roo.data.JsonReader({
16062 totalProperty: "results", // The property which contains the total dataset size (optional)
16063 root: "rows", // The property which contains an Array of row objects
16064 id: "id" // The property within each row object that provides an ID for the record (optional)
16068 * This would consume a JSON file like this:
16070 { 'results': 2, 'rows': [
16071 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16072 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16075 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16076 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16077 * paged from the remote server.
16078 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16079 * @cfg {String} root name of the property which contains the Array of row objects.
16080 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16081 * @cfg {Array} fields Array of field definition objects
16083 * Create a new JsonReader
16084 * @param {Object} meta Metadata configuration options
16085 * @param {Object} recordType Either an Array of field definition objects,
16086 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16088 Roo.data.JsonReader = function(meta, recordType){
16091 // set some defaults:
16092 Roo.applyIf(meta, {
16093 totalProperty: 'total',
16094 successProperty : 'success',
16099 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16101 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16103 readerType : 'Json',
16106 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16107 * Used by Store query builder to append _requestMeta to params.
16110 metaFromRemote : false,
16112 * This method is only used by a DataProxy which has retrieved data from a remote server.
16113 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16114 * @return {Object} data A data block which is used by an Roo.data.Store object as
16115 * a cache of Roo.data.Records.
16117 read : function(response){
16118 var json = response.responseText;
16120 var o = /* eval:var:o */ eval("("+json+")");
16122 throw {message: "JsonReader.read: Json object not found"};
16128 this.metaFromRemote = true;
16129 this.meta = o.metaData;
16130 this.recordType = Roo.data.Record.create(o.metaData.fields);
16131 this.onMetaChange(this.meta, this.recordType, o);
16133 return this.readRecords(o);
16136 // private function a store will implement
16137 onMetaChange : function(meta, recordType, o){
16144 simpleAccess: function(obj, subsc) {
16151 getJsonAccessor: function(){
16153 return function(expr) {
16155 return(re.test(expr))
16156 ? new Function("obj", "return obj." + expr)
16161 return Roo.emptyFn;
16166 * Create a data block containing Roo.data.Records from an XML document.
16167 * @param {Object} o An object which contains an Array of row objects in the property specified
16168 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16169 * which contains the total size of the dataset.
16170 * @return {Object} data A data block which is used by an Roo.data.Store object as
16171 * a cache of Roo.data.Records.
16173 readRecords : function(o){
16175 * After any data loads, the raw JSON data is available for further custom processing.
16179 var s = this.meta, Record = this.recordType,
16180 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16182 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16184 if(s.totalProperty) {
16185 this.getTotal = this.getJsonAccessor(s.totalProperty);
16187 if(s.successProperty) {
16188 this.getSuccess = this.getJsonAccessor(s.successProperty);
16190 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16192 var g = this.getJsonAccessor(s.id);
16193 this.getId = function(rec) {
16195 return (r === undefined || r === "") ? null : r;
16198 this.getId = function(){return null;};
16201 for(var jj = 0; jj < fl; jj++){
16203 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16204 this.ef[jj] = this.getJsonAccessor(map);
16208 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16209 if(s.totalProperty){
16210 var vt = parseInt(this.getTotal(o), 10);
16215 if(s.successProperty){
16216 var vs = this.getSuccess(o);
16217 if(vs === false || vs === 'false'){
16222 for(var i = 0; i < c; i++){
16225 var id = this.getId(n);
16226 for(var j = 0; j < fl; j++){
16228 var v = this.ef[j](n);
16230 Roo.log('missing convert for ' + f.name);
16234 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16236 var record = new Record(values, id);
16238 records[i] = record;
16244 totalRecords : totalRecords
16247 // used when loading children.. @see loadDataFromChildren
16248 toLoadData: function(rec)
16250 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16251 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16252 return { data : data, total : data.length };
16257 * Ext JS Library 1.1.1
16258 * Copyright(c) 2006-2007, Ext JS, LLC.
16260 * Originally Released Under LGPL - original licence link has changed is not relivant.
16263 * <script type="text/javascript">
16267 * @class Roo.data.ArrayReader
16268 * @extends Roo.data.DataReader
16269 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16270 * Each element of that Array represents a row of data fields. The
16271 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16272 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16276 var RecordDef = Roo.data.Record.create([
16277 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16278 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16280 var myReader = new Roo.data.ArrayReader({
16281 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16285 * This would consume an Array like this:
16287 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16291 * Create a new JsonReader
16292 * @param {Object} meta Metadata configuration options.
16293 * @param {Object|Array} recordType Either an Array of field definition objects
16295 * @cfg {Array} fields Array of field definition objects
16296 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16297 * as specified to {@link Roo.data.Record#create},
16298 * or an {@link Roo.data.Record} object
16301 * created using {@link Roo.data.Record#create}.
16303 Roo.data.ArrayReader = function(meta, recordType)
16305 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16308 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16311 * Create a data block containing Roo.data.Records from an XML document.
16312 * @param {Object} o An Array of row objects which represents the dataset.
16313 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16314 * a cache of Roo.data.Records.
16316 readRecords : function(o)
16318 var sid = this.meta ? this.meta.id : null;
16319 var recordType = this.recordType, fields = recordType.prototype.fields;
16322 for(var i = 0; i < root.length; i++){
16325 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16326 for(var j = 0, jlen = fields.length; j < jlen; j++){
16327 var f = fields.items[j];
16328 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16329 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16331 values[f.name] = v;
16333 var record = new recordType(values, id);
16335 records[records.length] = record;
16339 totalRecords : records.length
16342 // used when loading children.. @see loadDataFromChildren
16343 toLoadData: function(rec)
16345 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16346 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16357 * @class Roo.bootstrap.ComboBox
16358 * @extends Roo.bootstrap.TriggerField
16359 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16360 * @cfg {Boolean} append (true|false) default false
16361 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16362 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16363 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16364 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16365 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16366 * @cfg {Boolean} animate default true
16367 * @cfg {Boolean} emptyResultText only for touch device
16368 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16369 * @cfg {String} emptyTitle default ''
16370 * @cfg {Number} width fixed with? experimental
16372 * Create a new ComboBox.
16373 * @param {Object} config Configuration options
16375 Roo.bootstrap.ComboBox = function(config){
16376 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16380 * Fires when the dropdown list is expanded
16381 * @param {Roo.bootstrap.ComboBox} combo This combo box
16386 * Fires when the dropdown list is collapsed
16387 * @param {Roo.bootstrap.ComboBox} combo This combo box
16391 * @event beforeselect
16392 * Fires before a list item is selected. Return false to cancel the selection.
16393 * @param {Roo.bootstrap.ComboBox} combo This combo box
16394 * @param {Roo.data.Record} record The data record returned from the underlying store
16395 * @param {Number} index The index of the selected item in the dropdown list
16397 'beforeselect' : true,
16400 * Fires when a list item is selected
16401 * @param {Roo.bootstrap.ComboBox} combo This combo box
16402 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16403 * @param {Number} index The index of the selected item in the dropdown list
16407 * @event beforequery
16408 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16409 * The event object passed has these properties:
16410 * @param {Roo.bootstrap.ComboBox} combo This combo box
16411 * @param {String} query The query
16412 * @param {Boolean} forceAll true to force "all" query
16413 * @param {Boolean} cancel true to cancel the query
16414 * @param {Object} e The query event object
16416 'beforequery': true,
16419 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16420 * @param {Roo.bootstrap.ComboBox} combo This combo box
16425 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16426 * @param {Roo.bootstrap.ComboBox} combo This combo box
16427 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16432 * Fires when the remove value from the combobox array
16433 * @param {Roo.bootstrap.ComboBox} combo This combo box
16437 * @event afterremove
16438 * Fires when the remove value from the combobox array
16439 * @param {Roo.bootstrap.ComboBox} combo This combo box
16441 'afterremove' : true,
16443 * @event specialfilter
16444 * Fires when specialfilter
16445 * @param {Roo.bootstrap.ComboBox} combo This combo box
16447 'specialfilter' : true,
16450 * Fires when tick the element
16451 * @param {Roo.bootstrap.ComboBox} combo This combo box
16455 * @event touchviewdisplay
16456 * Fires when touch view require special display (default is using displayField)
16457 * @param {Roo.bootstrap.ComboBox} combo This combo box
16458 * @param {Object} cfg set html .
16460 'touchviewdisplay' : true
16465 this.tickItems = [];
16467 this.selectedIndex = -1;
16468 if(this.mode == 'local'){
16469 if(config.queryDelay === undefined){
16470 this.queryDelay = 10;
16472 if(config.minChars === undefined){
16478 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16481 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16482 * rendering into an Roo.Editor, defaults to false)
16485 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16486 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16489 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16492 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16493 * the dropdown list (defaults to undefined, with no header element)
16497 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16501 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16503 listWidth: undefined,
16505 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16506 * mode = 'remote' or 'text' if mode = 'local')
16508 displayField: undefined,
16511 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16512 * mode = 'remote' or 'value' if mode = 'local').
16513 * Note: use of a valueField requires the user make a selection
16514 * in order for a value to be mapped.
16516 valueField: undefined,
16518 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16523 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16524 * field's data value (defaults to the underlying DOM element's name)
16526 hiddenName: undefined,
16528 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16532 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16534 selectedClass: 'active',
16537 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16541 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16542 * anchor positions (defaults to 'tl-bl')
16544 listAlign: 'tl-bl?',
16546 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16550 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16551 * query specified by the allQuery config option (defaults to 'query')
16553 triggerAction: 'query',
16555 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16556 * (defaults to 4, does not apply if editable = false)
16560 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16561 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16565 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16566 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16570 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16571 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16575 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16576 * when editable = true (defaults to false)
16578 selectOnFocus:false,
16580 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16582 queryParam: 'query',
16584 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16585 * when mode = 'remote' (defaults to 'Loading...')
16587 loadingText: 'Loading...',
16589 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16593 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16597 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16598 * traditional select (defaults to true)
16602 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16606 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16610 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16611 * listWidth has a higher value)
16615 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16616 * allow the user to set arbitrary text into the field (defaults to false)
16618 forceSelection:false,
16620 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16621 * if typeAhead = true (defaults to 250)
16623 typeAheadDelay : 250,
16625 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16626 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16628 valueNotFoundText : undefined,
16630 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16632 blockFocus : false,
16635 * @cfg {Boolean} disableClear Disable showing of clear button.
16637 disableClear : false,
16639 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16641 alwaysQuery : false,
16644 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16649 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16651 invalidClass : "has-warning",
16654 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16656 validClass : "has-success",
16659 * @cfg {Boolean} specialFilter (true|false) special filter default false
16661 specialFilter : false,
16664 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16666 mobileTouchView : true,
16669 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16671 useNativeIOS : false,
16674 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16676 mobile_restrict_height : false,
16678 ios_options : false,
16690 btnPosition : 'right',
16691 triggerList : true,
16692 showToggleBtn : true,
16694 emptyResultText: 'Empty',
16695 triggerText : 'Select',
16699 // element that contains real text value.. (when hidden is used..)
16701 getAutoCreate : function()
16706 * Render classic select for iso
16709 if(Roo.isIOS && this.useNativeIOS){
16710 cfg = this.getAutoCreateNativeIOS();
16718 if(Roo.isTouch && this.mobileTouchView){
16719 cfg = this.getAutoCreateTouchView();
16726 if(!this.tickable){
16727 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16732 * ComboBox with tickable selections
16735 var align = this.labelAlign || this.parentLabelAlign();
16738 cls : 'form-group roo-combobox-tickable' //input-group
16741 var btn_text_select = '';
16742 var btn_text_done = '';
16743 var btn_text_cancel = '';
16745 if (this.btn_text_show) {
16746 btn_text_select = 'Select';
16747 btn_text_done = 'Done';
16748 btn_text_cancel = 'Cancel';
16753 cls : 'tickable-buttons',
16758 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16759 //html : this.triggerText
16760 html: btn_text_select
16766 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16768 html: btn_text_done
16774 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16776 html: btn_text_cancel
16782 buttons.cn.unshift({
16784 cls: 'roo-select2-search-field-input'
16790 Roo.each(buttons.cn, function(c){
16792 c.cls += ' btn-' + _this.size;
16795 if (_this.disabled) {
16802 style : 'display: contents',
16807 cls: 'form-hidden-field'
16811 cls: 'roo-select2-choices',
16815 cls: 'roo-select2-search-field',
16826 cls: 'roo-select2-container input-group roo-select2-container-multi',
16832 // cls: 'typeahead typeahead-long dropdown-menu',
16833 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16838 if(this.hasFeedback && !this.allowBlank){
16842 cls: 'glyphicon form-control-feedback'
16845 combobox.cn.push(feedback);
16852 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16853 tooltip : 'This field is required'
16855 if (Roo.bootstrap.version == 4) {
16858 style : 'display:none'
16861 if (align ==='left' && this.fieldLabel.length) {
16863 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16870 cls : 'control-label col-form-label',
16871 html : this.fieldLabel
16883 var labelCfg = cfg.cn[1];
16884 var contentCfg = cfg.cn[2];
16887 if(this.indicatorpos == 'right'){
16893 cls : 'control-label col-form-label',
16897 html : this.fieldLabel
16913 labelCfg = cfg.cn[0];
16914 contentCfg = cfg.cn[1];
16918 if(this.labelWidth > 12){
16919 labelCfg.style = "width: " + this.labelWidth + 'px';
16921 if(this.width * 1 > 0){
16922 contentCfg.style = "width: " + this.width + 'px';
16924 if(this.labelWidth < 13 && this.labelmd == 0){
16925 this.labelmd = this.labelWidth;
16928 if(this.labellg > 0){
16929 labelCfg.cls += ' col-lg-' + this.labellg;
16930 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16933 if(this.labelmd > 0){
16934 labelCfg.cls += ' col-md-' + this.labelmd;
16935 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16938 if(this.labelsm > 0){
16939 labelCfg.cls += ' col-sm-' + this.labelsm;
16940 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16943 if(this.labelxs > 0){
16944 labelCfg.cls += ' col-xs-' + this.labelxs;
16945 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16949 } else if ( this.fieldLabel.length) {
16950 // Roo.log(" label");
16955 //cls : 'input-group-addon',
16956 html : this.fieldLabel
16961 if(this.indicatorpos == 'right'){
16965 //cls : 'input-group-addon',
16966 html : this.fieldLabel
16976 // Roo.log(" no label && no align");
16983 ['xs','sm','md','lg'].map(function(size){
16984 if (settings[size]) {
16985 cfg.cls += ' col-' + size + '-' + settings[size];
16993 _initEventsCalled : false,
16996 initEvents: function()
16998 if (this._initEventsCalled) { // as we call render... prevent looping...
17001 this._initEventsCalled = true;
17004 throw "can not find store for combo";
17007 this.indicator = this.indicatorEl();
17009 this.store = Roo.factory(this.store, Roo.data);
17010 this.store.parent = this;
17012 // if we are building from html. then this element is so complex, that we can not really
17013 // use the rendered HTML.
17014 // so we have to trash and replace the previous code.
17015 if (Roo.XComponent.build_from_html) {
17016 // remove this element....
17017 var e = this.el.dom, k=0;
17018 while (e ) { e = e.previousSibling; ++k;}
17023 this.rendered = false;
17025 this.render(this.parent().getChildContainer(true), k);
17028 if(Roo.isIOS && this.useNativeIOS){
17029 this.initIOSView();
17037 if(Roo.isTouch && this.mobileTouchView){
17038 this.initTouchView();
17043 this.initTickableEvents();
17047 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17049 if(this.hiddenName){
17051 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17053 this.hiddenField.dom.value =
17054 this.hiddenValue !== undefined ? this.hiddenValue :
17055 this.value !== undefined ? this.value : '';
17057 // prevent input submission
17058 this.el.dom.removeAttribute('name');
17059 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17064 // this.el.dom.setAttribute('autocomplete', 'off');
17067 var cls = 'x-combo-list';
17069 //this.list = new Roo.Layer({
17070 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17076 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17077 _this.list.setWidth(lw);
17080 this.list.on('mouseover', this.onViewOver, this);
17081 this.list.on('mousemove', this.onViewMove, this);
17082 this.list.on('scroll', this.onViewScroll, this);
17085 this.list.swallowEvent('mousewheel');
17086 this.assetHeight = 0;
17089 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17090 this.assetHeight += this.header.getHeight();
17093 this.innerList = this.list.createChild({cls:cls+'-inner'});
17094 this.innerList.on('mouseover', this.onViewOver, this);
17095 this.innerList.on('mousemove', this.onViewMove, this);
17096 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17098 if(this.allowBlank && !this.pageSize && !this.disableClear){
17099 this.footer = this.list.createChild({cls:cls+'-ft'});
17100 this.pageTb = new Roo.Toolbar(this.footer);
17104 this.footer = this.list.createChild({cls:cls+'-ft'});
17105 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17106 {pageSize: this.pageSize});
17110 if (this.pageTb && this.allowBlank && !this.disableClear) {
17112 this.pageTb.add(new Roo.Toolbar.Fill(), {
17113 cls: 'x-btn-icon x-btn-clear',
17115 handler: function()
17118 _this.clearValue();
17119 _this.onSelect(false, -1);
17124 this.assetHeight += this.footer.getHeight();
17129 this.tpl = Roo.bootstrap.version == 4 ?
17130 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17131 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17134 this.view = new Roo.View(this.list, this.tpl, {
17135 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17137 //this.view.wrapEl.setDisplayed(false);
17138 this.view.on('click', this.onViewClick, this);
17141 this.store.on('beforeload', this.onBeforeLoad, this);
17142 this.store.on('load', this.onLoad, this);
17143 this.store.on('loadexception', this.onLoadException, this);
17145 if(this.resizable){
17146 this.resizer = new Roo.Resizable(this.list, {
17147 pinned:true, handles:'se'
17149 this.resizer.on('resize', function(r, w, h){
17150 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17151 this.listWidth = w;
17152 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17153 this.restrictHeight();
17155 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17158 if(!this.editable){
17159 this.editable = true;
17160 this.setEditable(false);
17165 if (typeof(this.events.add.listeners) != 'undefined') {
17167 this.addicon = this.wrap.createChild(
17168 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17170 this.addicon.on('click', function(e) {
17171 this.fireEvent('add', this);
17174 if (typeof(this.events.edit.listeners) != 'undefined') {
17176 this.editicon = this.wrap.createChild(
17177 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17178 if (this.addicon) {
17179 this.editicon.setStyle('margin-left', '40px');
17181 this.editicon.on('click', function(e) {
17183 // we fire even if inothing is selected..
17184 this.fireEvent('edit', this, this.lastData );
17190 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17191 "up" : function(e){
17192 this.inKeyMode = true;
17196 "down" : function(e){
17197 if(!this.isExpanded()){
17198 this.onTriggerClick();
17200 this.inKeyMode = true;
17205 "enter" : function(e){
17206 // this.onViewClick();
17210 if(this.fireEvent("specialkey", this, e)){
17211 this.onViewClick(false);
17217 "esc" : function(e){
17221 "tab" : function(e){
17224 if(this.fireEvent("specialkey", this, e)){
17225 this.onViewClick(false);
17233 doRelay : function(foo, bar, hname){
17234 if(hname == 'down' || this.scope.isExpanded()){
17235 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17244 this.queryDelay = Math.max(this.queryDelay || 10,
17245 this.mode == 'local' ? 10 : 250);
17248 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17250 if(this.typeAhead){
17251 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17253 if(this.editable !== false){
17254 this.inputEl().on("keyup", this.onKeyUp, this);
17256 if(this.forceSelection){
17257 this.inputEl().on('blur', this.doForce, this);
17261 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17262 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17266 initTickableEvents: function()
17270 if(this.hiddenName){
17272 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17274 this.hiddenField.dom.value =
17275 this.hiddenValue !== undefined ? this.hiddenValue :
17276 this.value !== undefined ? this.value : '';
17278 // prevent input submission
17279 this.el.dom.removeAttribute('name');
17280 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17285 // this.list = this.el.select('ul.dropdown-menu',true).first();
17287 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17288 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17289 if(this.triggerList){
17290 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17293 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17294 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17296 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17297 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17299 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17300 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17302 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17303 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17304 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17307 this.cancelBtn.hide();
17312 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17313 _this.list.setWidth(lw);
17316 this.list.on('mouseover', this.onViewOver, this);
17317 this.list.on('mousemove', this.onViewMove, this);
17319 this.list.on('scroll', this.onViewScroll, this);
17322 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17323 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17326 this.view = new Roo.View(this.list, this.tpl, {
17331 selectedClass: this.selectedClass
17334 //this.view.wrapEl.setDisplayed(false);
17335 this.view.on('click', this.onViewClick, this);
17339 this.store.on('beforeload', this.onBeforeLoad, this);
17340 this.store.on('load', this.onLoad, this);
17341 this.store.on('loadexception', this.onLoadException, this);
17344 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17345 "up" : function(e){
17346 this.inKeyMode = true;
17350 "down" : function(e){
17351 this.inKeyMode = true;
17355 "enter" : function(e){
17356 if(this.fireEvent("specialkey", this, e)){
17357 this.onViewClick(false);
17363 "esc" : function(e){
17364 this.onTickableFooterButtonClick(e, false, false);
17367 "tab" : function(e){
17368 this.fireEvent("specialkey", this, e);
17370 this.onTickableFooterButtonClick(e, false, false);
17377 doRelay : function(e, fn, key){
17378 if(this.scope.isExpanded()){
17379 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17388 this.queryDelay = Math.max(this.queryDelay || 10,
17389 this.mode == 'local' ? 10 : 250);
17392 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17394 if(this.typeAhead){
17395 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17398 if(this.editable !== false){
17399 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17402 this.indicator = this.indicatorEl();
17404 if(this.indicator){
17405 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17406 this.indicator.hide();
17411 onDestroy : function(){
17413 this.view.setStore(null);
17414 this.view.el.removeAllListeners();
17415 this.view.el.remove();
17416 this.view.purgeListeners();
17419 this.list.dom.innerHTML = '';
17423 this.store.un('beforeload', this.onBeforeLoad, this);
17424 this.store.un('load', this.onLoad, this);
17425 this.store.un('loadexception', this.onLoadException, this);
17427 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17431 fireKey : function(e){
17432 if(e.isNavKeyPress() && !this.list.isVisible()){
17433 this.fireEvent("specialkey", this, e);
17438 onResize: function(w, h)
17442 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17444 // if(typeof w != 'number'){
17445 // // we do not handle it!?!?
17448 // var tw = this.trigger.getWidth();
17449 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17450 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17452 // this.inputEl().setWidth( this.adjustWidth('input', x));
17454 // //this.trigger.setStyle('left', x+'px');
17456 // if(this.list && this.listWidth === undefined){
17457 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17458 // this.list.setWidth(lw);
17459 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17467 * Allow or prevent the user from directly editing the field text. If false is passed,
17468 * the user will only be able to select from the items defined in the dropdown list. This method
17469 * is the runtime equivalent of setting the 'editable' config option at config time.
17470 * @param {Boolean} value True to allow the user to directly edit the field text
17472 setEditable : function(value){
17473 if(value == this.editable){
17476 this.editable = value;
17478 this.inputEl().dom.setAttribute('readOnly', true);
17479 this.inputEl().on('mousedown', this.onTriggerClick, this);
17480 this.inputEl().addClass('x-combo-noedit');
17482 this.inputEl().dom.removeAttribute('readOnly');
17483 this.inputEl().un('mousedown', this.onTriggerClick, this);
17484 this.inputEl().removeClass('x-combo-noedit');
17490 onBeforeLoad : function(combo,opts){
17491 if(!this.hasFocus){
17495 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17497 this.restrictHeight();
17498 this.selectedIndex = -1;
17502 onLoad : function(){
17504 this.hasQuery = false;
17506 if(!this.hasFocus){
17510 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17511 this.loading.hide();
17514 if(this.store.getCount() > 0){
17517 this.restrictHeight();
17518 if(this.lastQuery == this.allQuery){
17519 if(this.editable && !this.tickable){
17520 this.inputEl().dom.select();
17524 !this.selectByValue(this.value, true) &&
17527 !this.store.lastOptions ||
17528 typeof(this.store.lastOptions.add) == 'undefined' ||
17529 this.store.lastOptions.add != true
17532 this.select(0, true);
17535 if(this.autoFocus){
17538 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17539 this.taTask.delay(this.typeAheadDelay);
17543 this.onEmptyResults();
17549 onLoadException : function()
17551 this.hasQuery = false;
17553 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17554 this.loading.hide();
17557 if(this.tickable && this.editable){
17562 // only causes errors at present
17563 //Roo.log(this.store.reader.jsonData);
17564 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17566 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17572 onTypeAhead : function(){
17573 if(this.store.getCount() > 0){
17574 var r = this.store.getAt(0);
17575 var newValue = r.data[this.displayField];
17576 var len = newValue.length;
17577 var selStart = this.getRawValue().length;
17579 if(selStart != len){
17580 this.setRawValue(newValue);
17581 this.selectText(selStart, newValue.length);
17587 onSelect : function(record, index){
17589 if(this.fireEvent('beforeselect', this, record, index) !== false){
17591 this.setFromData(index > -1 ? record.data : false);
17594 this.fireEvent('select', this, record, index);
17599 * Returns the currently selected field value or empty string if no value is set.
17600 * @return {String} value The selected value
17602 getValue : function()
17604 if(Roo.isIOS && this.useNativeIOS){
17605 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17609 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17612 if(this.valueField){
17613 return typeof this.value != 'undefined' ? this.value : '';
17615 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17619 getRawValue : function()
17621 if(Roo.isIOS && this.useNativeIOS){
17622 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17625 var v = this.inputEl().getValue();
17631 * Clears any text/value currently set in the field
17633 clearValue : function(){
17635 if(this.hiddenField){
17636 this.hiddenField.dom.value = '';
17639 this.setRawValue('');
17640 this.lastSelectionText = '';
17641 this.lastData = false;
17643 var close = this.closeTriggerEl();
17654 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17655 * will be displayed in the field. If the value does not match the data value of an existing item,
17656 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17657 * Otherwise the field will be blank (although the value will still be set).
17658 * @param {String} value The value to match
17660 setValue : function(v)
17662 if(Roo.isIOS && this.useNativeIOS){
17663 this.setIOSValue(v);
17673 if(this.valueField){
17674 var r = this.findRecord(this.valueField, v);
17676 text = r.data[this.displayField];
17677 }else if(this.valueNotFoundText !== undefined){
17678 text = this.valueNotFoundText;
17681 this.lastSelectionText = text;
17682 if(this.hiddenField){
17683 this.hiddenField.dom.value = v;
17685 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17688 var close = this.closeTriggerEl();
17691 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17697 * @property {Object} the last set data for the element
17702 * Sets the value of the field based on a object which is related to the record format for the store.
17703 * @param {Object} value the value to set as. or false on reset?
17705 setFromData : function(o){
17712 var dv = ''; // display value
17713 var vv = ''; // value value..
17715 if (this.displayField) {
17716 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17718 // this is an error condition!!!
17719 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17722 if(this.valueField){
17723 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17726 var close = this.closeTriggerEl();
17729 if(dv.length || vv * 1 > 0){
17731 this.blockFocus=true;
17737 if(this.hiddenField){
17738 this.hiddenField.dom.value = vv;
17740 this.lastSelectionText = dv;
17741 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17745 // no hidden field.. - we store the value in 'value', but still display
17746 // display field!!!!
17747 this.lastSelectionText = dv;
17748 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17755 reset : function(){
17756 // overridden so that last data is reset..
17763 this.setValue(this.originalValue);
17764 //this.clearInvalid();
17765 this.lastData = false;
17767 this.view.clearSelections();
17773 findRecord : function(prop, value){
17775 if(this.store.getCount() > 0){
17776 this.store.each(function(r){
17777 if(r.data[prop] == value){
17787 getName: function()
17789 // returns hidden if it's set..
17790 if (!this.rendered) {return ''};
17791 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17795 onViewMove : function(e, t){
17796 this.inKeyMode = false;
17800 onViewOver : function(e, t){
17801 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17804 var item = this.view.findItemFromChild(t);
17807 var index = this.view.indexOf(item);
17808 this.select(index, false);
17813 onViewClick : function(view, doFocus, el, e)
17815 var index = this.view.getSelectedIndexes()[0];
17817 var r = this.store.getAt(index);
17821 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17828 Roo.each(this.tickItems, function(v,k){
17830 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17832 _this.tickItems.splice(k, 1);
17834 if(typeof(e) == 'undefined' && view == false){
17835 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17847 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17848 this.tickItems.push(r.data);
17851 if(typeof(e) == 'undefined' && view == false){
17852 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17859 this.onSelect(r, index);
17861 if(doFocus !== false && !this.blockFocus){
17862 this.inputEl().focus();
17867 restrictHeight : function(){
17868 //this.innerList.dom.style.height = '';
17869 //var inner = this.innerList.dom;
17870 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17871 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17872 //this.list.beginUpdate();
17873 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17874 this.list.alignTo(this.inputEl(), this.listAlign);
17875 this.list.alignTo(this.inputEl(), this.listAlign);
17876 //this.list.endUpdate();
17880 onEmptyResults : function(){
17882 if(this.tickable && this.editable){
17883 this.hasFocus = false;
17884 this.restrictHeight();
17892 * Returns true if the dropdown list is expanded, else false.
17894 isExpanded : function(){
17895 return this.list.isVisible();
17899 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17900 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17901 * @param {String} value The data value of the item to select
17902 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17903 * selected item if it is not currently in view (defaults to true)
17904 * @return {Boolean} True if the value matched an item in the list, else false
17906 selectByValue : function(v, scrollIntoView){
17907 if(v !== undefined && v !== null){
17908 var r = this.findRecord(this.valueField || this.displayField, v);
17910 this.select(this.store.indexOf(r), scrollIntoView);
17918 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17919 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17920 * @param {Number} index The zero-based index of the list item to select
17921 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17922 * selected item if it is not currently in view (defaults to true)
17924 select : function(index, scrollIntoView){
17925 this.selectedIndex = index;
17926 this.view.select(index);
17927 if(scrollIntoView !== false){
17928 var el = this.view.getNode(index);
17930 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17933 this.list.scrollChildIntoView(el, false);
17939 selectNext : function(){
17940 var ct = this.store.getCount();
17942 if(this.selectedIndex == -1){
17944 }else if(this.selectedIndex < ct-1){
17945 this.select(this.selectedIndex+1);
17951 selectPrev : function(){
17952 var ct = this.store.getCount();
17954 if(this.selectedIndex == -1){
17956 }else if(this.selectedIndex != 0){
17957 this.select(this.selectedIndex-1);
17963 onKeyUp : function(e){
17964 if(this.editable !== false && !e.isSpecialKey()){
17965 this.lastKey = e.getKey();
17966 this.dqTask.delay(this.queryDelay);
17971 validateBlur : function(){
17972 return !this.list || !this.list.isVisible();
17976 initQuery : function(){
17978 var v = this.getRawValue();
17980 if(this.tickable && this.editable){
17981 v = this.tickableInputEl().getValue();
17988 doForce : function(){
17989 if(this.inputEl().dom.value.length > 0){
17990 this.inputEl().dom.value =
17991 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17997 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17998 * query allowing the query action to be canceled if needed.
17999 * @param {String} query The SQL query to execute
18000 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18001 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18002 * saved in the current store (defaults to false)
18004 doQuery : function(q, forceAll){
18006 if(q === undefined || q === null){
18011 forceAll: forceAll,
18015 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18020 forceAll = qe.forceAll;
18021 if(forceAll === true || (q.length >= this.minChars)){
18023 this.hasQuery = true;
18025 if(this.lastQuery != q || this.alwaysQuery){
18026 this.lastQuery = q;
18027 if(this.mode == 'local'){
18028 this.selectedIndex = -1;
18030 this.store.clearFilter();
18033 if(this.specialFilter){
18034 this.fireEvent('specialfilter', this);
18039 this.store.filter(this.displayField, q);
18042 this.store.fireEvent("datachanged", this.store);
18049 this.store.baseParams[this.queryParam] = q;
18051 var options = {params : this.getParams(q)};
18054 options.add = true;
18055 options.params.start = this.page * this.pageSize;
18058 this.store.load(options);
18061 * this code will make the page width larger, at the beginning, the list not align correctly,
18062 * we should expand the list on onLoad
18063 * so command out it
18068 this.selectedIndex = -1;
18073 this.loadNext = false;
18077 getParams : function(q){
18079 //p[this.queryParam] = q;
18083 p.limit = this.pageSize;
18089 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18091 collapse : function(){
18092 if(!this.isExpanded()){
18098 this.hasFocus = false;
18102 this.cancelBtn.hide();
18103 this.trigger.show();
18106 this.tickableInputEl().dom.value = '';
18107 this.tickableInputEl().blur();
18112 Roo.get(document).un('mousedown', this.collapseIf, this);
18113 Roo.get(document).un('mousewheel', this.collapseIf, this);
18114 if (!this.editable) {
18115 Roo.get(document).un('keydown', this.listKeyPress, this);
18117 this.fireEvent('collapse', this);
18123 collapseIf : function(e){
18124 var in_combo = e.within(this.el);
18125 var in_list = e.within(this.list);
18126 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18128 if (in_combo || in_list || is_list) {
18129 //e.stopPropagation();
18134 this.onTickableFooterButtonClick(e, false, false);
18142 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18144 expand : function(){
18146 if(this.isExpanded() || !this.hasFocus){
18150 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18151 this.list.setWidth(lw);
18157 this.restrictHeight();
18161 this.tickItems = Roo.apply([], this.item);
18164 this.cancelBtn.show();
18165 this.trigger.hide();
18168 this.tickableInputEl().focus();
18173 Roo.get(document).on('mousedown', this.collapseIf, this);
18174 Roo.get(document).on('mousewheel', this.collapseIf, this);
18175 if (!this.editable) {
18176 Roo.get(document).on('keydown', this.listKeyPress, this);
18179 this.fireEvent('expand', this);
18183 // Implements the default empty TriggerField.onTriggerClick function
18184 onTriggerClick : function(e)
18186 Roo.log('trigger click');
18188 if(this.disabled || !this.triggerList){
18193 this.loadNext = false;
18195 if(this.isExpanded()){
18197 if (!this.blockFocus) {
18198 this.inputEl().focus();
18202 this.hasFocus = true;
18203 if(this.triggerAction == 'all') {
18204 this.doQuery(this.allQuery, true);
18206 this.doQuery(this.getRawValue());
18208 if (!this.blockFocus) {
18209 this.inputEl().focus();
18214 onTickableTriggerClick : function(e)
18221 this.loadNext = false;
18222 this.hasFocus = true;
18224 if(this.triggerAction == 'all') {
18225 this.doQuery(this.allQuery, true);
18227 this.doQuery(this.getRawValue());
18231 onSearchFieldClick : function(e)
18233 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18234 this.onTickableFooterButtonClick(e, false, false);
18238 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18243 this.loadNext = false;
18244 this.hasFocus = true;
18246 if(this.triggerAction == 'all') {
18247 this.doQuery(this.allQuery, true);
18249 this.doQuery(this.getRawValue());
18253 listKeyPress : function(e)
18255 //Roo.log('listkeypress');
18256 // scroll to first matching element based on key pres..
18257 if (e.isSpecialKey()) {
18260 var k = String.fromCharCode(e.getKey()).toUpperCase();
18263 var csel = this.view.getSelectedNodes();
18264 var cselitem = false;
18266 var ix = this.view.indexOf(csel[0]);
18267 cselitem = this.store.getAt(ix);
18268 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18274 this.store.each(function(v) {
18276 // start at existing selection.
18277 if (cselitem.id == v.id) {
18283 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18284 match = this.store.indexOf(v);
18290 if (match === false) {
18291 return true; // no more action?
18294 this.view.select(match);
18295 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18296 sn.scrollIntoView(sn.dom.parentNode, false);
18299 onViewScroll : function(e, t){
18301 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){
18305 this.hasQuery = true;
18307 this.loading = this.list.select('.loading', true).first();
18309 if(this.loading === null){
18310 this.list.createChild({
18312 cls: 'loading roo-select2-more-results roo-select2-active',
18313 html: 'Loading more results...'
18316 this.loading = this.list.select('.loading', true).first();
18318 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18320 this.loading.hide();
18323 this.loading.show();
18328 this.loadNext = true;
18330 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18335 addItem : function(o)
18337 var dv = ''; // display value
18339 if (this.displayField) {
18340 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18342 // this is an error condition!!!
18343 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18350 var choice = this.choices.createChild({
18352 cls: 'roo-select2-search-choice',
18361 cls: 'roo-select2-search-choice-close fa fa-times',
18366 }, this.searchField);
18368 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18370 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18378 this.inputEl().dom.value = '';
18383 onRemoveItem : function(e, _self, o)
18385 e.preventDefault();
18387 this.lastItem = Roo.apply([], this.item);
18389 var index = this.item.indexOf(o.data) * 1;
18392 Roo.log('not this item?!');
18396 this.item.splice(index, 1);
18401 this.fireEvent('remove', this, e);
18407 syncValue : function()
18409 if(!this.item.length){
18416 Roo.each(this.item, function(i){
18417 if(_this.valueField){
18418 value.push(i[_this.valueField]);
18425 this.value = value.join(',');
18427 if(this.hiddenField){
18428 this.hiddenField.dom.value = this.value;
18431 this.store.fireEvent("datachanged", this.store);
18436 clearItem : function()
18438 if(!this.multiple){
18444 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18452 if(this.tickable && !Roo.isTouch){
18453 this.view.refresh();
18457 inputEl: function ()
18459 if(Roo.isIOS && this.useNativeIOS){
18460 return this.el.select('select.roo-ios-select', true).first();
18463 if(Roo.isTouch && this.mobileTouchView){
18464 return this.el.select('input.form-control',true).first();
18468 return this.searchField;
18471 return this.el.select('input.form-control',true).first();
18474 onTickableFooterButtonClick : function(e, btn, el)
18476 e.preventDefault();
18478 this.lastItem = Roo.apply([], this.item);
18480 if(btn && btn.name == 'cancel'){
18481 this.tickItems = Roo.apply([], this.item);
18490 Roo.each(this.tickItems, function(o){
18498 validate : function()
18500 if(this.getVisibilityEl().hasClass('hidden')){
18504 var v = this.getRawValue();
18507 v = this.getValue();
18510 if(this.disabled || this.allowBlank || v.length){
18515 this.markInvalid();
18519 tickableInputEl : function()
18521 if(!this.tickable || !this.editable){
18522 return this.inputEl();
18525 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18529 getAutoCreateTouchView : function()
18534 cls: 'form-group' //input-group
18540 type : this.inputType,
18541 cls : 'form-control x-combo-noedit',
18542 autocomplete: 'new-password',
18543 placeholder : this.placeholder || '',
18548 input.name = this.name;
18552 input.cls += ' input-' + this.size;
18555 if (this.disabled) {
18556 input.disabled = true;
18560 cls : 'roo-combobox-wrap',
18567 inputblock.cls += ' input-group';
18569 inputblock.cn.unshift({
18571 cls : 'input-group-addon input-group-prepend input-group-text',
18576 if(this.removable && !this.multiple){
18577 inputblock.cls += ' roo-removable';
18579 inputblock.cn.push({
18582 cls : 'roo-combo-removable-btn close'
18586 if(this.hasFeedback && !this.allowBlank){
18588 inputblock.cls += ' has-feedback';
18590 inputblock.cn.push({
18592 cls: 'glyphicon form-control-feedback'
18599 inputblock.cls += (this.before) ? '' : ' input-group';
18601 inputblock.cn.push({
18603 cls : 'input-group-addon input-group-append input-group-text',
18609 var ibwrap = inputblock;
18614 cls: 'roo-select2-choices',
18618 cls: 'roo-select2-search-field',
18631 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18636 cls: 'form-hidden-field'
18642 if(!this.multiple && this.showToggleBtn){
18648 if (this.caret != false) {
18651 cls: 'fa fa-' + this.caret
18658 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18660 Roo.bootstrap.version == 3 ? caret : '',
18663 cls: 'combobox-clear',
18677 combobox.cls += ' roo-select2-container-multi';
18680 var required = this.allowBlank ? {
18682 style: 'display: none'
18685 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18686 tooltip : 'This field is required'
18689 var align = this.labelAlign || this.parentLabelAlign();
18691 if (align ==='left' && this.fieldLabel.length) {
18697 cls : 'control-label col-form-label',
18698 html : this.fieldLabel
18702 cls : 'roo-combobox-wrap ',
18709 var labelCfg = cfg.cn[1];
18710 var contentCfg = cfg.cn[2];
18713 if(this.indicatorpos == 'right'){
18718 cls : 'control-label col-form-label',
18722 html : this.fieldLabel
18728 cls : "roo-combobox-wrap ",
18736 labelCfg = cfg.cn[0];
18737 contentCfg = cfg.cn[1];
18742 if(this.labelWidth > 12){
18743 labelCfg.style = "width: " + this.labelWidth + 'px';
18746 if(this.labelWidth < 13 && this.labelmd == 0){
18747 this.labelmd = this.labelWidth;
18750 if(this.labellg > 0){
18751 labelCfg.cls += ' col-lg-' + this.labellg;
18752 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18755 if(this.labelmd > 0){
18756 labelCfg.cls += ' col-md-' + this.labelmd;
18757 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18760 if(this.labelsm > 0){
18761 labelCfg.cls += ' col-sm-' + this.labelsm;
18762 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18765 if(this.labelxs > 0){
18766 labelCfg.cls += ' col-xs-' + this.labelxs;
18767 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18771 } else if ( this.fieldLabel.length) {
18776 cls : 'control-label',
18777 html : this.fieldLabel
18788 if(this.indicatorpos == 'right'){
18792 cls : 'control-label',
18793 html : this.fieldLabel,
18811 var settings = this;
18813 ['xs','sm','md','lg'].map(function(size){
18814 if (settings[size]) {
18815 cfg.cls += ' col-' + size + '-' + settings[size];
18822 initTouchView : function()
18824 this.renderTouchView();
18826 this.touchViewEl.on('scroll', function(){
18827 this.el.dom.scrollTop = 0;
18830 this.originalValue = this.getValue();
18832 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18834 this.inputEl().on("click", this.showTouchView, this);
18835 if (this.triggerEl) {
18836 this.triggerEl.on("click", this.showTouchView, this);
18840 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18841 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18843 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18845 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18846 this.store.on('load', this.onTouchViewLoad, this);
18847 this.store.on('loadexception', this.onTouchViewLoadException, this);
18849 if(this.hiddenName){
18851 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18853 this.hiddenField.dom.value =
18854 this.hiddenValue !== undefined ? this.hiddenValue :
18855 this.value !== undefined ? this.value : '';
18857 this.el.dom.removeAttribute('name');
18858 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18862 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18863 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18866 if(this.removable && !this.multiple){
18867 var close = this.closeTriggerEl();
18869 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18870 close.on('click', this.removeBtnClick, this, close);
18874 * fix the bug in Safari iOS8
18876 this.inputEl().on("focus", function(e){
18877 document.activeElement.blur();
18880 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18887 renderTouchView : function()
18889 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18890 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18892 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18893 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18895 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18896 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18897 this.touchViewBodyEl.setStyle('overflow', 'auto');
18899 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18900 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18902 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18903 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18907 showTouchView : function()
18913 this.touchViewHeaderEl.hide();
18915 if(this.modalTitle.length){
18916 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18917 this.touchViewHeaderEl.show();
18920 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18921 this.touchViewEl.show();
18923 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18925 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18926 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18928 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18930 if(this.modalTitle.length){
18931 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18934 this.touchViewBodyEl.setHeight(bodyHeight);
18938 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18940 this.touchViewEl.addClass(['in','show']);
18943 if(this._touchViewMask){
18944 Roo.get(document.body).addClass("x-body-masked");
18945 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18946 this._touchViewMask.setStyle('z-index', 10000);
18947 this._touchViewMask.addClass('show');
18950 this.doTouchViewQuery();
18954 hideTouchView : function()
18956 this.touchViewEl.removeClass(['in','show']);
18960 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18962 this.touchViewEl.setStyle('display', 'none');
18965 if(this._touchViewMask){
18966 this._touchViewMask.removeClass('show');
18967 Roo.get(document.body).removeClass("x-body-masked");
18971 setTouchViewValue : function()
18978 Roo.each(this.tickItems, function(o){
18983 this.hideTouchView();
18986 doTouchViewQuery : function()
18995 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18999 if(!this.alwaysQuery || this.mode == 'local'){
19000 this.onTouchViewLoad();
19007 onTouchViewBeforeLoad : function(combo,opts)
19013 onTouchViewLoad : function()
19015 if(this.store.getCount() < 1){
19016 this.onTouchViewEmptyResults();
19020 this.clearTouchView();
19022 var rawValue = this.getRawValue();
19024 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19026 this.tickItems = [];
19028 this.store.data.each(function(d, rowIndex){
19029 var row = this.touchViewListGroup.createChild(template);
19031 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19032 row.addClass(d.data.cls);
19035 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19038 html : d.data[this.displayField]
19041 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19042 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19045 row.removeClass('selected');
19046 if(!this.multiple && this.valueField &&
19047 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19050 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19051 row.addClass('selected');
19054 if(this.multiple && this.valueField &&
19055 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19059 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19060 this.tickItems.push(d.data);
19063 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19067 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19069 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19071 if(this.modalTitle.length){
19072 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19075 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19077 if(this.mobile_restrict_height && listHeight < bodyHeight){
19078 this.touchViewBodyEl.setHeight(listHeight);
19083 if(firstChecked && listHeight > bodyHeight){
19084 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19089 onTouchViewLoadException : function()
19091 this.hideTouchView();
19094 onTouchViewEmptyResults : function()
19096 this.clearTouchView();
19098 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19100 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19104 clearTouchView : function()
19106 this.touchViewListGroup.dom.innerHTML = '';
19109 onTouchViewClick : function(e, el, o)
19111 e.preventDefault();
19114 var rowIndex = o.rowIndex;
19116 var r = this.store.getAt(rowIndex);
19118 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19120 if(!this.multiple){
19121 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19122 c.dom.removeAttribute('checked');
19125 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19127 this.setFromData(r.data);
19129 var close = this.closeTriggerEl();
19135 this.hideTouchView();
19137 this.fireEvent('select', this, r, rowIndex);
19142 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19143 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19144 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19148 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19149 this.addItem(r.data);
19150 this.tickItems.push(r.data);
19154 getAutoCreateNativeIOS : function()
19157 cls: 'form-group' //input-group,
19162 cls : 'roo-ios-select'
19166 combobox.name = this.name;
19169 if (this.disabled) {
19170 combobox.disabled = true;
19173 var settings = this;
19175 ['xs','sm','md','lg'].map(function(size){
19176 if (settings[size]) {
19177 cfg.cls += ' col-' + size + '-' + settings[size];
19187 initIOSView : function()
19189 this.store.on('load', this.onIOSViewLoad, this);
19194 onIOSViewLoad : function()
19196 if(this.store.getCount() < 1){
19200 this.clearIOSView();
19202 if(this.allowBlank) {
19204 var default_text = '-- SELECT --';
19206 if(this.placeholder.length){
19207 default_text = this.placeholder;
19210 if(this.emptyTitle.length){
19211 default_text += ' - ' + this.emptyTitle + ' -';
19214 var opt = this.inputEl().createChild({
19217 html : default_text
19221 o[this.valueField] = 0;
19222 o[this.displayField] = default_text;
19224 this.ios_options.push({
19231 this.store.data.each(function(d, rowIndex){
19235 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19236 html = d.data[this.displayField];
19241 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19242 value = d.data[this.valueField];
19251 if(this.value == d.data[this.valueField]){
19252 option['selected'] = true;
19255 var opt = this.inputEl().createChild(option);
19257 this.ios_options.push({
19264 this.inputEl().on('change', function(){
19265 this.fireEvent('select', this);
19270 clearIOSView: function()
19272 this.inputEl().dom.innerHTML = '';
19274 this.ios_options = [];
19277 setIOSValue: function(v)
19281 if(!this.ios_options){
19285 Roo.each(this.ios_options, function(opts){
19287 opts.el.dom.removeAttribute('selected');
19289 if(opts.data[this.valueField] != v){
19293 opts.el.dom.setAttribute('selected', true);
19299 * @cfg {Boolean} grow
19303 * @cfg {Number} growMin
19307 * @cfg {Number} growMax
19316 Roo.apply(Roo.bootstrap.ComboBox, {
19320 cls: 'modal-header',
19342 cls: 'list-group-item',
19346 cls: 'roo-combobox-list-group-item-value'
19350 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19364 listItemCheckbox : {
19366 cls: 'list-group-item',
19370 cls: 'roo-combobox-list-group-item-value'
19374 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19390 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19395 cls: 'modal-footer',
19403 cls: 'col-xs-6 text-left',
19406 cls: 'btn btn-danger roo-touch-view-cancel',
19412 cls: 'col-xs-6 text-right',
19415 cls: 'btn btn-success roo-touch-view-ok',
19426 Roo.apply(Roo.bootstrap.ComboBox, {
19428 touchViewTemplate : {
19430 cls: 'modal fade roo-combobox-touch-view',
19434 cls: 'modal-dialog',
19435 style : 'position:fixed', // we have to fix position....
19439 cls: 'modal-content',
19441 Roo.bootstrap.ComboBox.header,
19442 Roo.bootstrap.ComboBox.body,
19443 Roo.bootstrap.ComboBox.footer
19452 * Ext JS Library 1.1.1
19453 * Copyright(c) 2006-2007, Ext JS, LLC.
19455 * Originally Released Under LGPL - original licence link has changed is not relivant.
19458 * <script type="text/javascript">
19463 * @extends Roo.util.Observable
19464 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19465 * This class also supports single and multi selection modes. <br>
19466 * Create a data model bound view:
19468 var store = new Roo.data.Store(...);
19470 var view = new Roo.View({
19472 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19474 singleSelect: true,
19475 selectedClass: "ydataview-selected",
19479 // listen for node click?
19480 view.on("click", function(vw, index, node, e){
19481 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19485 dataModel.load("foobar.xml");
19487 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19489 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19490 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19492 * Note: old style constructor is still suported (container, template, config)
19495 * Create a new View
19496 * @param {Object} config The config object
19499 Roo.View = function(config, depreciated_tpl, depreciated_config){
19501 this.parent = false;
19503 if (typeof(depreciated_tpl) == 'undefined') {
19504 // new way.. - universal constructor.
19505 Roo.apply(this, config);
19506 this.el = Roo.get(this.el);
19509 this.el = Roo.get(config);
19510 this.tpl = depreciated_tpl;
19511 Roo.apply(this, depreciated_config);
19513 this.wrapEl = this.el.wrap().wrap();
19514 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19517 if(typeof(this.tpl) == "string"){
19518 this.tpl = new Roo.Template(this.tpl);
19520 // support xtype ctors..
19521 this.tpl = new Roo.factory(this.tpl, Roo);
19525 this.tpl.compile();
19530 * @event beforeclick
19531 * Fires before a click is processed. Returns false to cancel the default action.
19532 * @param {Roo.View} this
19533 * @param {Number} index The index of the target node
19534 * @param {HTMLElement} node The target node
19535 * @param {Roo.EventObject} e The raw event object
19537 "beforeclick" : true,
19540 * Fires when a template node is clicked.
19541 * @param {Roo.View} this
19542 * @param {Number} index The index of the target node
19543 * @param {HTMLElement} node The target node
19544 * @param {Roo.EventObject} e The raw event object
19549 * Fires when a template node is double clicked.
19550 * @param {Roo.View} this
19551 * @param {Number} index The index of the target node
19552 * @param {HTMLElement} node The target node
19553 * @param {Roo.EventObject} e The raw event object
19557 * @event contextmenu
19558 * Fires when a template node is right clicked.
19559 * @param {Roo.View} this
19560 * @param {Number} index The index of the target node
19561 * @param {HTMLElement} node The target node
19562 * @param {Roo.EventObject} e The raw event object
19564 "contextmenu" : true,
19566 * @event selectionchange
19567 * Fires when the selected nodes change.
19568 * @param {Roo.View} this
19569 * @param {Array} selections Array of the selected nodes
19571 "selectionchange" : true,
19574 * @event beforeselect
19575 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19576 * @param {Roo.View} this
19577 * @param {HTMLElement} node The node to be selected
19578 * @param {Array} selections Array of currently selected nodes
19580 "beforeselect" : true,
19582 * @event preparedata
19583 * Fires on every row to render, to allow you to change the data.
19584 * @param {Roo.View} this
19585 * @param {Object} data to be rendered (change this)
19587 "preparedata" : true
19595 "click": this.onClick,
19596 "dblclick": this.onDblClick,
19597 "contextmenu": this.onContextMenu,
19601 this.selections = [];
19603 this.cmp = new Roo.CompositeElementLite([]);
19605 this.store = Roo.factory(this.store, Roo.data);
19606 this.setStore(this.store, true);
19609 if ( this.footer && this.footer.xtype) {
19611 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19613 this.footer.dataSource = this.store;
19614 this.footer.container = fctr;
19615 this.footer = Roo.factory(this.footer, Roo);
19616 fctr.insertFirst(this.el);
19618 // this is a bit insane - as the paging toolbar seems to detach the el..
19619 // dom.parentNode.parentNode.parentNode
19620 // they get detached?
19624 Roo.View.superclass.constructor.call(this);
19629 Roo.extend(Roo.View, Roo.util.Observable, {
19632 * @cfg {Roo.data.Store} store Data store to load data from.
19637 * @cfg {String|Roo.Element} el The container element.
19642 * @cfg {String|Roo.Template} tpl The template used by this View
19646 * @cfg {String} dataName the named area of the template to use as the data area
19647 * Works with domtemplates roo-name="name"
19651 * @cfg {String} selectedClass The css class to add to selected nodes
19653 selectedClass : "x-view-selected",
19655 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19660 * @cfg {String} text to display on mask (default Loading)
19664 * @cfg {Boolean} multiSelect Allow multiple selection
19666 multiSelect : false,
19668 * @cfg {Boolean} singleSelect Allow single selection
19670 singleSelect: false,
19673 * @cfg {Boolean} toggleSelect - selecting
19675 toggleSelect : false,
19678 * @cfg {Boolean} tickable - selecting
19683 * Returns the element this view is bound to.
19684 * @return {Roo.Element}
19686 getEl : function(){
19687 return this.wrapEl;
19693 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19695 refresh : function(){
19696 //Roo.log('refresh');
19699 // if we are using something like 'domtemplate', then
19700 // the what gets used is:
19701 // t.applySubtemplate(NAME, data, wrapping data..)
19702 // the outer template then get' applied with
19703 // the store 'extra data'
19704 // and the body get's added to the
19705 // roo-name="data" node?
19706 // <span class='roo-tpl-{name}'></span> ?????
19710 this.clearSelections();
19711 this.el.update("");
19713 var records = this.store.getRange();
19714 if(records.length < 1) {
19716 // is this valid?? = should it render a template??
19718 this.el.update(this.emptyText);
19722 if (this.dataName) {
19723 this.el.update(t.apply(this.store.meta)); //????
19724 el = this.el.child('.roo-tpl-' + this.dataName);
19727 for(var i = 0, len = records.length; i < len; i++){
19728 var data = this.prepareData(records[i].data, i, records[i]);
19729 this.fireEvent("preparedata", this, data, i, records[i]);
19731 var d = Roo.apply({}, data);
19734 Roo.apply(d, {'roo-id' : Roo.id()});
19738 Roo.each(this.parent.item, function(item){
19739 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19742 Roo.apply(d, {'roo-data-checked' : 'checked'});
19746 html[html.length] = Roo.util.Format.trim(
19748 t.applySubtemplate(this.dataName, d, this.store.meta) :
19755 el.update(html.join(""));
19756 this.nodes = el.dom.childNodes;
19757 this.updateIndexes(0);
19762 * Function to override to reformat the data that is sent to
19763 * the template for each node.
19764 * DEPRICATED - use the preparedata event handler.
19765 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19766 * a JSON object for an UpdateManager bound view).
19768 prepareData : function(data, index, record)
19770 this.fireEvent("preparedata", this, data, index, record);
19774 onUpdate : function(ds, record){
19775 // Roo.log('on update');
19776 this.clearSelections();
19777 var index = this.store.indexOf(record);
19778 var n = this.nodes[index];
19779 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19780 n.parentNode.removeChild(n);
19781 this.updateIndexes(index, index);
19787 onAdd : function(ds, records, index)
19789 //Roo.log(['on Add', ds, records, index] );
19790 this.clearSelections();
19791 if(this.nodes.length == 0){
19795 var n = this.nodes[index];
19796 for(var i = 0, len = records.length; i < len; i++){
19797 var d = this.prepareData(records[i].data, i, records[i]);
19799 this.tpl.insertBefore(n, d);
19802 this.tpl.append(this.el, d);
19805 this.updateIndexes(index);
19808 onRemove : function(ds, record, index){
19809 // Roo.log('onRemove');
19810 this.clearSelections();
19811 var el = this.dataName ?
19812 this.el.child('.roo-tpl-' + this.dataName) :
19815 el.dom.removeChild(this.nodes[index]);
19816 this.updateIndexes(index);
19820 * Refresh an individual node.
19821 * @param {Number} index
19823 refreshNode : function(index){
19824 this.onUpdate(this.store, this.store.getAt(index));
19827 updateIndexes : function(startIndex, endIndex){
19828 var ns = this.nodes;
19829 startIndex = startIndex || 0;
19830 endIndex = endIndex || ns.length - 1;
19831 for(var i = startIndex; i <= endIndex; i++){
19832 ns[i].nodeIndex = i;
19837 * Changes the data store this view uses and refresh the view.
19838 * @param {Store} store
19840 setStore : function(store, initial){
19841 if(!initial && this.store){
19842 this.store.un("datachanged", this.refresh);
19843 this.store.un("add", this.onAdd);
19844 this.store.un("remove", this.onRemove);
19845 this.store.un("update", this.onUpdate);
19846 this.store.un("clear", this.refresh);
19847 this.store.un("beforeload", this.onBeforeLoad);
19848 this.store.un("load", this.onLoad);
19849 this.store.un("loadexception", this.onLoad);
19853 store.on("datachanged", this.refresh, this);
19854 store.on("add", this.onAdd, this);
19855 store.on("remove", this.onRemove, this);
19856 store.on("update", this.onUpdate, this);
19857 store.on("clear", this.refresh, this);
19858 store.on("beforeload", this.onBeforeLoad, this);
19859 store.on("load", this.onLoad, this);
19860 store.on("loadexception", this.onLoad, this);
19868 * onbeforeLoad - masks the loading area.
19871 onBeforeLoad : function(store,opts)
19873 //Roo.log('onBeforeLoad');
19875 this.el.update("");
19877 this.el.mask(this.mask ? this.mask : "Loading" );
19879 onLoad : function ()
19886 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19887 * @param {HTMLElement} node
19888 * @return {HTMLElement} The template node
19890 findItemFromChild : function(node){
19891 var el = this.dataName ?
19892 this.el.child('.roo-tpl-' + this.dataName,true) :
19895 if(!node || node.parentNode == el){
19898 var p = node.parentNode;
19899 while(p && p != el){
19900 if(p.parentNode == el){
19909 onClick : function(e){
19910 var item = this.findItemFromChild(e.getTarget());
19912 var index = this.indexOf(item);
19913 if(this.onItemClick(item, index, e) !== false){
19914 this.fireEvent("click", this, index, item, e);
19917 this.clearSelections();
19922 onContextMenu : function(e){
19923 var item = this.findItemFromChild(e.getTarget());
19925 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19930 onDblClick : function(e){
19931 var item = this.findItemFromChild(e.getTarget());
19933 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19937 onItemClick : function(item, index, e)
19939 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19942 if (this.toggleSelect) {
19943 var m = this.isSelected(item) ? 'unselect' : 'select';
19946 _t[m](item, true, false);
19949 if(this.multiSelect || this.singleSelect){
19950 if(this.multiSelect && e.shiftKey && this.lastSelection){
19951 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19953 this.select(item, this.multiSelect && e.ctrlKey);
19954 this.lastSelection = item;
19957 if(!this.tickable){
19958 e.preventDefault();
19966 * Get the number of selected nodes.
19969 getSelectionCount : function(){
19970 return this.selections.length;
19974 * Get the currently selected nodes.
19975 * @return {Array} An array of HTMLElements
19977 getSelectedNodes : function(){
19978 return this.selections;
19982 * Get the indexes of the selected nodes.
19985 getSelectedIndexes : function(){
19986 var indexes = [], s = this.selections;
19987 for(var i = 0, len = s.length; i < len; i++){
19988 indexes.push(s[i].nodeIndex);
19994 * Clear all selections
19995 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19997 clearSelections : function(suppressEvent){
19998 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19999 this.cmp.elements = this.selections;
20000 this.cmp.removeClass(this.selectedClass);
20001 this.selections = [];
20002 if(!suppressEvent){
20003 this.fireEvent("selectionchange", this, this.selections);
20009 * Returns true if the passed node is selected
20010 * @param {HTMLElement/Number} node The node or node index
20011 * @return {Boolean}
20013 isSelected : function(node){
20014 var s = this.selections;
20018 node = this.getNode(node);
20019 return s.indexOf(node) !== -1;
20024 * @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
20025 * @param {Boolean} keepExisting (optional) true to keep existing selections
20026 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20028 select : function(nodeInfo, keepExisting, suppressEvent){
20029 if(nodeInfo instanceof Array){
20031 this.clearSelections(true);
20033 for(var i = 0, len = nodeInfo.length; i < len; i++){
20034 this.select(nodeInfo[i], true, true);
20038 var node = this.getNode(nodeInfo);
20039 if(!node || this.isSelected(node)){
20040 return; // already selected.
20043 this.clearSelections(true);
20046 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20047 Roo.fly(node).addClass(this.selectedClass);
20048 this.selections.push(node);
20049 if(!suppressEvent){
20050 this.fireEvent("selectionchange", this, this.selections);
20058 * @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
20059 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20060 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20062 unselect : function(nodeInfo, keepExisting, suppressEvent)
20064 if(nodeInfo instanceof Array){
20065 Roo.each(this.selections, function(s) {
20066 this.unselect(s, nodeInfo);
20070 var node = this.getNode(nodeInfo);
20071 if(!node || !this.isSelected(node)){
20072 //Roo.log("not selected");
20073 return; // not selected.
20077 Roo.each(this.selections, function(s) {
20079 Roo.fly(node).removeClass(this.selectedClass);
20086 this.selections= ns;
20087 this.fireEvent("selectionchange", this, this.selections);
20091 * Gets a template node.
20092 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20093 * @return {HTMLElement} The node or null if it wasn't found
20095 getNode : function(nodeInfo){
20096 if(typeof nodeInfo == "string"){
20097 return document.getElementById(nodeInfo);
20098 }else if(typeof nodeInfo == "number"){
20099 return this.nodes[nodeInfo];
20105 * Gets a range template nodes.
20106 * @param {Number} startIndex
20107 * @param {Number} endIndex
20108 * @return {Array} An array of nodes
20110 getNodes : function(start, end){
20111 var ns = this.nodes;
20112 start = start || 0;
20113 end = typeof end == "undefined" ? ns.length - 1 : end;
20116 for(var i = start; i <= end; i++){
20120 for(var i = start; i >= end; i--){
20128 * Finds the index of the passed node
20129 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20130 * @return {Number} The index of the node or -1
20132 indexOf : function(node){
20133 node = this.getNode(node);
20134 if(typeof node.nodeIndex == "number"){
20135 return node.nodeIndex;
20137 var ns = this.nodes;
20138 for(var i = 0, len = ns.length; i < len; i++){
20149 * based on jquery fullcalendar
20153 Roo.bootstrap = Roo.bootstrap || {};
20155 * @class Roo.bootstrap.Calendar
20156 * @extends Roo.bootstrap.Component
20157 * Bootstrap Calendar class
20158 * @cfg {Boolean} loadMask (true|false) default false
20159 * @cfg {Object} header generate the user specific header of the calendar, default false
20162 * Create a new Container
20163 * @param {Object} config The config object
20168 Roo.bootstrap.Calendar = function(config){
20169 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20173 * Fires when a date is selected
20174 * @param {DatePicker} this
20175 * @param {Date} date The selected date
20179 * @event monthchange
20180 * Fires when the displayed month changes
20181 * @param {DatePicker} this
20182 * @param {Date} date The selected month
20184 'monthchange': true,
20186 * @event evententer
20187 * Fires when mouse over an event
20188 * @param {Calendar} this
20189 * @param {event} Event
20191 'evententer': true,
20193 * @event eventleave
20194 * Fires when the mouse leaves an
20195 * @param {Calendar} this
20198 'eventleave': true,
20200 * @event eventclick
20201 * Fires when the mouse click an
20202 * @param {Calendar} this
20211 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20214 * @cfg {Roo.data.Store} store
20215 * The data source for the calendar
20219 * @cfg {Number} startDay
20220 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20228 getAutoCreate : function(){
20231 var fc_button = function(name, corner, style, content ) {
20232 return Roo.apply({},{
20234 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20236 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20239 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20250 style : 'width:100%',
20257 cls : 'fc-header-left',
20259 fc_button('prev', 'left', 'arrow', '‹' ),
20260 fc_button('next', 'right', 'arrow', '›' ),
20261 { tag: 'span', cls: 'fc-header-space' },
20262 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20270 cls : 'fc-header-center',
20274 cls: 'fc-header-title',
20277 html : 'month / year'
20285 cls : 'fc-header-right',
20287 /* fc_button('month', 'left', '', 'month' ),
20288 fc_button('week', '', '', 'week' ),
20289 fc_button('day', 'right', '', 'day' )
20301 header = this.header;
20304 var cal_heads = function() {
20306 // fixme - handle this.
20308 for (var i =0; i < Date.dayNames.length; i++) {
20309 var d = Date.dayNames[i];
20312 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20313 html : d.substring(0,3)
20317 ret[0].cls += ' fc-first';
20318 ret[6].cls += ' fc-last';
20321 var cal_cell = function(n) {
20324 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20329 cls: 'fc-day-number',
20333 cls: 'fc-day-content',
20337 style: 'position: relative;' // height: 17px;
20349 var cal_rows = function() {
20352 for (var r = 0; r < 6; r++) {
20359 for (var i =0; i < Date.dayNames.length; i++) {
20360 var d = Date.dayNames[i];
20361 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20364 row.cn[0].cls+=' fc-first';
20365 row.cn[0].cn[0].style = 'min-height:90px';
20366 row.cn[6].cls+=' fc-last';
20370 ret[0].cls += ' fc-first';
20371 ret[4].cls += ' fc-prev-last';
20372 ret[5].cls += ' fc-last';
20379 cls: 'fc-border-separate',
20380 style : 'width:100%',
20388 cls : 'fc-first fc-last',
20406 cls : 'fc-content',
20407 style : "position: relative;",
20410 cls : 'fc-view fc-view-month fc-grid',
20411 style : 'position: relative',
20412 unselectable : 'on',
20415 cls : 'fc-event-container',
20416 style : 'position:absolute;z-index:8;top:0;left:0;'
20434 initEvents : function()
20437 throw "can not find store for calendar";
20443 style: "text-align:center",
20447 style: "background-color:white;width:50%;margin:250 auto",
20451 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20462 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20464 var size = this.el.select('.fc-content', true).first().getSize();
20465 this.maskEl.setSize(size.width, size.height);
20466 this.maskEl.enableDisplayMode("block");
20467 if(!this.loadMask){
20468 this.maskEl.hide();
20471 this.store = Roo.factory(this.store, Roo.data);
20472 this.store.on('load', this.onLoad, this);
20473 this.store.on('beforeload', this.onBeforeLoad, this);
20477 this.cells = this.el.select('.fc-day',true);
20478 //Roo.log(this.cells);
20479 this.textNodes = this.el.query('.fc-day-number');
20480 this.cells.addClassOnOver('fc-state-hover');
20482 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20483 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20484 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20485 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20487 this.on('monthchange', this.onMonthChange, this);
20489 this.update(new Date().clearTime());
20492 resize : function() {
20493 var sz = this.el.getSize();
20495 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20496 this.el.select('.fc-day-content div',true).setHeight(34);
20501 showPrevMonth : function(e){
20502 this.update(this.activeDate.add("mo", -1));
20504 showToday : function(e){
20505 this.update(new Date().clearTime());
20508 showNextMonth : function(e){
20509 this.update(this.activeDate.add("mo", 1));
20513 showPrevYear : function(){
20514 this.update(this.activeDate.add("y", -1));
20518 showNextYear : function(){
20519 this.update(this.activeDate.add("y", 1));
20524 update : function(date)
20526 var vd = this.activeDate;
20527 this.activeDate = date;
20528 // if(vd && this.el){
20529 // var t = date.getTime();
20530 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20531 // Roo.log('using add remove');
20533 // this.fireEvent('monthchange', this, date);
20535 // this.cells.removeClass("fc-state-highlight");
20536 // this.cells.each(function(c){
20537 // if(c.dateValue == t){
20538 // c.addClass("fc-state-highlight");
20539 // setTimeout(function(){
20540 // try{c.dom.firstChild.focus();}catch(e){}
20550 var days = date.getDaysInMonth();
20552 var firstOfMonth = date.getFirstDateOfMonth();
20553 var startingPos = firstOfMonth.getDay()-this.startDay;
20555 if(startingPos < this.startDay){
20559 var pm = date.add(Date.MONTH, -1);
20560 var prevStart = pm.getDaysInMonth()-startingPos;
20562 this.cells = this.el.select('.fc-day',true);
20563 this.textNodes = this.el.query('.fc-day-number');
20564 this.cells.addClassOnOver('fc-state-hover');
20566 var cells = this.cells.elements;
20567 var textEls = this.textNodes;
20569 Roo.each(cells, function(cell){
20570 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20573 days += startingPos;
20575 // convert everything to numbers so it's fast
20576 var day = 86400000;
20577 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20580 //Roo.log(prevStart);
20582 var today = new Date().clearTime().getTime();
20583 var sel = date.clearTime().getTime();
20584 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20585 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20586 var ddMatch = this.disabledDatesRE;
20587 var ddText = this.disabledDatesText;
20588 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20589 var ddaysText = this.disabledDaysText;
20590 var format = this.format;
20592 var setCellClass = function(cal, cell){
20596 //Roo.log('set Cell Class');
20598 var t = d.getTime();
20602 cell.dateValue = t;
20604 cell.className += " fc-today";
20605 cell.className += " fc-state-highlight";
20606 cell.title = cal.todayText;
20609 // disable highlight in other month..
20610 //cell.className += " fc-state-highlight";
20615 cell.className = " fc-state-disabled";
20616 cell.title = cal.minText;
20620 cell.className = " fc-state-disabled";
20621 cell.title = cal.maxText;
20625 if(ddays.indexOf(d.getDay()) != -1){
20626 cell.title = ddaysText;
20627 cell.className = " fc-state-disabled";
20630 if(ddMatch && format){
20631 var fvalue = d.dateFormat(format);
20632 if(ddMatch.test(fvalue)){
20633 cell.title = ddText.replace("%0", fvalue);
20634 cell.className = " fc-state-disabled";
20638 if (!cell.initialClassName) {
20639 cell.initialClassName = cell.dom.className;
20642 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20647 for(; i < startingPos; i++) {
20648 textEls[i].innerHTML = (++prevStart);
20649 d.setDate(d.getDate()+1);
20651 cells[i].className = "fc-past fc-other-month";
20652 setCellClass(this, cells[i]);
20657 for(; i < days; i++){
20658 intDay = i - startingPos + 1;
20659 textEls[i].innerHTML = (intDay);
20660 d.setDate(d.getDate()+1);
20662 cells[i].className = ''; // "x-date-active";
20663 setCellClass(this, cells[i]);
20667 for(; i < 42; i++) {
20668 textEls[i].innerHTML = (++extraDays);
20669 d.setDate(d.getDate()+1);
20671 cells[i].className = "fc-future fc-other-month";
20672 setCellClass(this, cells[i]);
20675 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20677 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20679 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20680 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20682 if(totalRows != 6){
20683 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20684 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20687 this.fireEvent('monthchange', this, date);
20691 if(!this.internalRender){
20692 var main = this.el.dom.firstChild;
20693 var w = main.offsetWidth;
20694 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20695 Roo.fly(main).setWidth(w);
20696 this.internalRender = true;
20697 // opera does not respect the auto grow header center column
20698 // then, after it gets a width opera refuses to recalculate
20699 // without a second pass
20700 if(Roo.isOpera && !this.secondPass){
20701 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20702 this.secondPass = true;
20703 this.update.defer(10, this, [date]);
20710 findCell : function(dt) {
20711 dt = dt.clearTime().getTime();
20713 this.cells.each(function(c){
20714 //Roo.log("check " +c.dateValue + '?=' + dt);
20715 if(c.dateValue == dt){
20725 findCells : function(ev) {
20726 var s = ev.start.clone().clearTime().getTime();
20728 var e= ev.end.clone().clearTime().getTime();
20731 this.cells.each(function(c){
20732 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20734 if(c.dateValue > e){
20737 if(c.dateValue < s){
20746 // findBestRow: function(cells)
20750 // for (var i =0 ; i < cells.length;i++) {
20751 // ret = Math.max(cells[i].rows || 0,ret);
20758 addItem : function(ev)
20760 // look for vertical location slot in
20761 var cells = this.findCells(ev);
20763 // ev.row = this.findBestRow(cells);
20765 // work out the location.
20769 for(var i =0; i < cells.length; i++) {
20771 cells[i].row = cells[0].row;
20774 cells[i].row = cells[i].row + 1;
20784 if (crow.start.getY() == cells[i].getY()) {
20786 crow.end = cells[i];
20803 cells[0].events.push(ev);
20805 this.calevents.push(ev);
20808 clearEvents: function() {
20810 if(!this.calevents){
20814 Roo.each(this.cells.elements, function(c){
20820 Roo.each(this.calevents, function(e) {
20821 Roo.each(e.els, function(el) {
20822 el.un('mouseenter' ,this.onEventEnter, this);
20823 el.un('mouseleave' ,this.onEventLeave, this);
20828 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20834 renderEvents: function()
20838 this.cells.each(function(c) {
20847 if(c.row != c.events.length){
20848 r = 4 - (4 - (c.row - c.events.length));
20851 c.events = ev.slice(0, r);
20852 c.more = ev.slice(r);
20854 if(c.more.length && c.more.length == 1){
20855 c.events.push(c.more.pop());
20858 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20862 this.cells.each(function(c) {
20864 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20867 for (var e = 0; e < c.events.length; e++){
20868 var ev = c.events[e];
20869 var rows = ev.rows;
20871 for(var i = 0; i < rows.length; i++) {
20873 // how many rows should it span..
20876 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20877 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20879 unselectable : "on",
20882 cls: 'fc-event-inner',
20886 // cls: 'fc-event-time',
20887 // html : cells.length > 1 ? '' : ev.time
20891 cls: 'fc-event-title',
20892 html : String.format('{0}', ev.title)
20899 cls: 'ui-resizable-handle ui-resizable-e',
20900 html : '  '
20907 cfg.cls += ' fc-event-start';
20909 if ((i+1) == rows.length) {
20910 cfg.cls += ' fc-event-end';
20913 var ctr = _this.el.select('.fc-event-container',true).first();
20914 var cg = ctr.createChild(cfg);
20916 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20917 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20919 var r = (c.more.length) ? 1 : 0;
20920 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20921 cg.setWidth(ebox.right - sbox.x -2);
20923 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20924 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20925 cg.on('click', _this.onEventClick, _this, ev);
20936 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20937 style : 'position: absolute',
20938 unselectable : "on",
20941 cls: 'fc-event-inner',
20945 cls: 'fc-event-title',
20953 cls: 'ui-resizable-handle ui-resizable-e',
20954 html : '  '
20960 var ctr = _this.el.select('.fc-event-container',true).first();
20961 var cg = ctr.createChild(cfg);
20963 var sbox = c.select('.fc-day-content',true).first().getBox();
20964 var ebox = c.select('.fc-day-content',true).first().getBox();
20966 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20967 cg.setWidth(ebox.right - sbox.x -2);
20969 cg.on('click', _this.onMoreEventClick, _this, c.more);
20979 onEventEnter: function (e, el,event,d) {
20980 this.fireEvent('evententer', this, el, event);
20983 onEventLeave: function (e, el,event,d) {
20984 this.fireEvent('eventleave', this, el, event);
20987 onEventClick: function (e, el,event,d) {
20988 this.fireEvent('eventclick', this, el, event);
20991 onMonthChange: function () {
20995 onMoreEventClick: function(e, el, more)
20999 this.calpopover.placement = 'right';
21000 this.calpopover.setTitle('More');
21002 this.calpopover.setContent('');
21004 var ctr = this.calpopover.el.select('.popover-content', true).first();
21006 Roo.each(more, function(m){
21008 cls : 'fc-event-hori fc-event-draggable',
21011 var cg = ctr.createChild(cfg);
21013 cg.on('click', _this.onEventClick, _this, m);
21016 this.calpopover.show(el);
21021 onLoad: function ()
21023 this.calevents = [];
21026 if(this.store.getCount() > 0){
21027 this.store.data.each(function(d){
21030 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21031 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21032 time : d.data.start_time,
21033 title : d.data.title,
21034 description : d.data.description,
21035 venue : d.data.venue
21040 this.renderEvents();
21042 if(this.calevents.length && this.loadMask){
21043 this.maskEl.hide();
21047 onBeforeLoad: function()
21049 this.clearEvents();
21051 this.maskEl.show();
21065 * @class Roo.bootstrap.Popover
21066 * @extends Roo.bootstrap.Component
21068 * Bootstrap Popover class
21069 * @cfg {String} html contents of the popover (or false to use children..)
21070 * @cfg {String} title of popover (or false to hide)
21071 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21072 * @cfg {String} trigger click || hover (or false to trigger manually)
21073 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21074 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21075 * - if false and it has a 'parent' then it will be automatically added to that element
21076 * - if string - Roo.get will be called
21077 * @cfg {Number} delay - delay before showing
21080 * Create a new Popover
21081 * @param {Object} config The config object
21084 Roo.bootstrap.Popover = function(config){
21085 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21091 * After the popover show
21093 * @param {Roo.bootstrap.Popover} this
21098 * After the popover hide
21100 * @param {Roo.bootstrap.Popover} this
21106 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21111 placement : 'right',
21112 trigger : 'hover', // hover
21118 can_build_overlaid : false,
21120 maskEl : false, // the mask element
21123 alignEl : false, // when show is called with an element - this get's stored.
21125 getChildContainer : function()
21127 return this.contentEl;
21130 getPopoverHeader : function()
21132 this.title = true; // flag not to hide it..
21133 this.headerEl.addClass('p-0');
21134 return this.headerEl
21138 getAutoCreate : function(){
21141 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21142 style: 'display:block',
21148 cls : 'popover-inner ',
21152 cls: 'popover-title popover-header',
21153 html : this.title === false ? '' : this.title
21156 cls : 'popover-content popover-body ' + (this.cls || ''),
21157 html : this.html || ''
21168 * @param {string} the title
21170 setTitle: function(str)
21174 this.headerEl.dom.innerHTML = str;
21179 * @param {string} the body content
21181 setContent: function(str)
21184 if (this.contentEl) {
21185 this.contentEl.dom.innerHTML = str;
21189 // as it get's added to the bottom of the page.
21190 onRender : function(ct, position)
21192 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21197 var cfg = Roo.apply({}, this.getAutoCreate());
21201 cfg.cls += ' ' + this.cls;
21204 cfg.style = this.style;
21206 //Roo.log("adding to ");
21207 this.el = Roo.get(document.body).createChild(cfg, position);
21208 // Roo.log(this.el);
21211 this.contentEl = this.el.select('.popover-content',true).first();
21212 this.headerEl = this.el.select('.popover-title',true).first();
21215 if(typeof(this.items) != 'undefined'){
21216 var items = this.items;
21219 for(var i =0;i < items.length;i++) {
21220 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21224 this.items = nitems;
21226 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21227 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21234 resizeMask : function()
21236 this.maskEl.setSize(
21237 Roo.lib.Dom.getViewWidth(true),
21238 Roo.lib.Dom.getViewHeight(true)
21242 initEvents : function()
21246 Roo.bootstrap.Popover.register(this);
21249 this.arrowEl = this.el.select('.arrow',true).first();
21250 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21251 this.el.enableDisplayMode('block');
21255 if (this.over === false && !this.parent()) {
21258 if (this.triggers === false) {
21263 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21264 var triggers = this.trigger ? this.trigger.split(' ') : [];
21265 Roo.each(triggers, function(trigger) {
21267 if (trigger == 'click') {
21268 on_el.on('click', this.toggle, this);
21269 } else if (trigger != 'manual') {
21270 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21271 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21273 on_el.on(eventIn ,this.enter, this);
21274 on_el.on(eventOut, this.leave, this);
21284 toggle : function () {
21285 this.hoverState == 'in' ? this.leave() : this.enter();
21288 enter : function () {
21290 clearTimeout(this.timeout);
21292 this.hoverState = 'in';
21294 if (!this.delay || !this.delay.show) {
21299 this.timeout = setTimeout(function () {
21300 if (_t.hoverState == 'in') {
21303 }, this.delay.show)
21306 leave : function() {
21307 clearTimeout(this.timeout);
21309 this.hoverState = 'out';
21311 if (!this.delay || !this.delay.hide) {
21316 this.timeout = setTimeout(function () {
21317 if (_t.hoverState == 'out') {
21320 }, this.delay.hide)
21324 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21325 * @param {string} (left|right|top|bottom) position
21327 show : function (on_el, placement)
21329 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21330 on_el = on_el || false; // default to false
21333 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21334 on_el = this.parent().el;
21335 } else if (this.over) {
21336 on_el = Roo.get(this.over);
21341 this.alignEl = Roo.get( on_el );
21344 this.render(document.body);
21350 if (this.title === false) {
21351 this.headerEl.hide();
21356 this.el.dom.style.display = 'block';
21359 if (this.alignEl) {
21360 this.updatePosition(this.placement, true);
21363 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21364 var es = this.el.getSize();
21365 var x = Roo.lib.Dom.getViewWidth()/2;
21366 var y = Roo.lib.Dom.getViewHeight()/2;
21367 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21372 //var arrow = this.el.select('.arrow',true).first();
21373 //arrow.set(align[2],
21375 this.el.addClass('in');
21379 this.hoverState = 'in';
21382 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21383 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21384 this.maskEl.dom.style.display = 'block';
21385 this.maskEl.addClass('show');
21387 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21389 this.fireEvent('show', this);
21393 * fire this manually after loading a grid in the table for example
21394 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21395 * @param {Boolean} try and move it if we cant get right position.
21397 updatePosition : function(placement, try_move)
21399 // allow for calling with no parameters
21400 placement = placement ? placement : this.placement;
21401 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21403 this.el.removeClass([
21404 'fade','top','bottom', 'left', 'right','in',
21405 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21407 this.el.addClass(placement + ' bs-popover-' + placement);
21409 if (!this.alignEl ) {
21413 switch (placement) {
21415 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21416 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21417 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21418 //normal display... or moved up/down.
21419 this.el.setXY(offset);
21420 var xy = this.alignEl.getAnchorXY('tr', false);
21422 this.arrowEl.setXY(xy);
21425 // continue through...
21426 return this.updatePosition('left', false);
21430 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21431 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21432 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21433 //normal display... or moved up/down.
21434 this.el.setXY(offset);
21435 var xy = this.alignEl.getAnchorXY('tl', false);
21436 xy[0]-=10;xy[1]+=5; // << fix me
21437 this.arrowEl.setXY(xy);
21441 return this.updatePosition('right', false);
21444 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21445 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21446 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21447 //normal display... or moved up/down.
21448 this.el.setXY(offset);
21449 var xy = this.alignEl.getAnchorXY('t', false);
21450 xy[1]-=10; // << fix me
21451 this.arrowEl.setXY(xy);
21455 return this.updatePosition('bottom', false);
21458 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21459 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21460 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21461 //normal display... or moved up/down.
21462 this.el.setXY(offset);
21463 var xy = this.alignEl.getAnchorXY('b', false);
21464 xy[1]+=2; // << fix me
21465 this.arrowEl.setXY(xy);
21469 return this.updatePosition('top', false);
21480 this.el.setXY([0,0]);
21481 this.el.removeClass('in');
21483 this.hoverState = null;
21484 this.maskEl.hide(); // always..
21485 this.fireEvent('hide', this);
21491 Roo.apply(Roo.bootstrap.Popover, {
21494 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21495 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21496 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21497 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21502 clickHander : false,
21506 onMouseDown : function(e)
21508 if (this.popups.length && !e.getTarget(".roo-popover")) {
21509 /// what is nothing is showing..
21518 register : function(popup)
21520 if (!Roo.bootstrap.Popover.clickHandler) {
21521 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21523 // hide other popups.
21524 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21525 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21526 this.hideAll(); //<< why?
21527 //this.popups.push(popup);
21529 hideAll : function()
21531 this.popups.forEach(function(p) {
21535 onShow : function() {
21536 Roo.bootstrap.Popover.popups.push(this);
21538 onHide : function() {
21539 Roo.bootstrap.Popover.popups.remove(this);
21545 * Card header - holder for the card header elements.
21550 * @class Roo.bootstrap.PopoverNav
21551 * @extends Roo.bootstrap.NavGroup
21552 * Bootstrap Popover header navigation class
21554 * Create a new Popover Header Navigation
21555 * @param {Object} config The config object
21558 Roo.bootstrap.PopoverNav = function(config){
21559 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21562 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21565 container_method : 'getPopoverHeader'
21583 * @class Roo.bootstrap.Progress
21584 * @extends Roo.bootstrap.Component
21585 * Bootstrap Progress class
21586 * @cfg {Boolean} striped striped of the progress bar
21587 * @cfg {Boolean} active animated of the progress bar
21591 * Create a new Progress
21592 * @param {Object} config The config object
21595 Roo.bootstrap.Progress = function(config){
21596 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21599 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21604 getAutoCreate : function(){
21612 cfg.cls += ' progress-striped';
21616 cfg.cls += ' active';
21635 * @class Roo.bootstrap.ProgressBar
21636 * @extends Roo.bootstrap.Component
21637 * Bootstrap ProgressBar class
21638 * @cfg {Number} aria_valuenow aria-value now
21639 * @cfg {Number} aria_valuemin aria-value min
21640 * @cfg {Number} aria_valuemax aria-value max
21641 * @cfg {String} label label for the progress bar
21642 * @cfg {String} panel (success | info | warning | danger )
21643 * @cfg {String} role role of the progress bar
21644 * @cfg {String} sr_only text
21648 * Create a new ProgressBar
21649 * @param {Object} config The config object
21652 Roo.bootstrap.ProgressBar = function(config){
21653 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21656 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21660 aria_valuemax : 100,
21666 getAutoCreate : function()
21671 cls: 'progress-bar',
21672 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21684 cfg.role = this.role;
21687 if(this.aria_valuenow){
21688 cfg['aria-valuenow'] = this.aria_valuenow;
21691 if(this.aria_valuemin){
21692 cfg['aria-valuemin'] = this.aria_valuemin;
21695 if(this.aria_valuemax){
21696 cfg['aria-valuemax'] = this.aria_valuemax;
21699 if(this.label && !this.sr_only){
21700 cfg.html = this.label;
21704 cfg.cls += ' progress-bar-' + this.panel;
21710 update : function(aria_valuenow)
21712 this.aria_valuenow = aria_valuenow;
21714 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21729 * @class Roo.bootstrap.TabGroup
21730 * @extends Roo.bootstrap.Column
21731 * Bootstrap Column class
21732 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21733 * @cfg {Boolean} carousel true to make the group behave like a carousel
21734 * @cfg {Boolean} bullets show bullets for the panels
21735 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21736 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21737 * @cfg {Boolean} showarrow (true|false) show arrow default true
21740 * Create a new TabGroup
21741 * @param {Object} config The config object
21744 Roo.bootstrap.TabGroup = function(config){
21745 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21747 this.navId = Roo.id();
21750 Roo.bootstrap.TabGroup.register(this);
21754 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21757 transition : false,
21762 slideOnTouch : false,
21765 getAutoCreate : function()
21767 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21769 cfg.cls += ' tab-content';
21771 if (this.carousel) {
21772 cfg.cls += ' carousel slide';
21775 cls : 'carousel-inner',
21779 if(this.bullets && !Roo.isTouch){
21782 cls : 'carousel-bullets',
21786 if(this.bullets_cls){
21787 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21794 cfg.cn[0].cn.push(bullets);
21797 if(this.showarrow){
21798 cfg.cn[0].cn.push({
21800 class : 'carousel-arrow',
21804 class : 'carousel-prev',
21808 class : 'fa fa-chevron-left'
21814 class : 'carousel-next',
21818 class : 'fa fa-chevron-right'
21831 initEvents: function()
21833 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21834 // this.el.on("touchstart", this.onTouchStart, this);
21837 if(this.autoslide){
21840 this.slideFn = window.setInterval(function() {
21841 _this.showPanelNext();
21845 if(this.showarrow){
21846 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21847 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21853 // onTouchStart : function(e, el, o)
21855 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21859 // this.showPanelNext();
21863 getChildContainer : function()
21865 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21869 * register a Navigation item
21870 * @param {Roo.bootstrap.NavItem} the navitem to add
21872 register : function(item)
21874 this.tabs.push( item);
21875 item.navId = this.navId; // not really needed..
21880 getActivePanel : function()
21883 Roo.each(this.tabs, function(t) {
21893 getPanelByName : function(n)
21896 Roo.each(this.tabs, function(t) {
21897 if (t.tabId == n) {
21905 indexOfPanel : function(p)
21908 Roo.each(this.tabs, function(t,i) {
21909 if (t.tabId == p.tabId) {
21918 * show a specific panel
21919 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21920 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21922 showPanel : function (pan)
21924 if(this.transition || typeof(pan) == 'undefined'){
21925 Roo.log("waiting for the transitionend");
21929 if (typeof(pan) == 'number') {
21930 pan = this.tabs[pan];
21933 if (typeof(pan) == 'string') {
21934 pan = this.getPanelByName(pan);
21937 var cur = this.getActivePanel();
21940 Roo.log('pan or acitve pan is undefined');
21944 if (pan.tabId == this.getActivePanel().tabId) {
21948 if (false === cur.fireEvent('beforedeactivate')) {
21952 if(this.bullets > 0 && !Roo.isTouch){
21953 this.setActiveBullet(this.indexOfPanel(pan));
21956 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21958 //class="carousel-item carousel-item-next carousel-item-left"
21960 this.transition = true;
21961 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21962 var lr = dir == 'next' ? 'left' : 'right';
21963 pan.el.addClass(dir); // or prev
21964 pan.el.addClass('carousel-item-' + dir); // or prev
21965 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21966 cur.el.addClass(lr); // or right
21967 pan.el.addClass(lr);
21968 cur.el.addClass('carousel-item-' +lr); // or right
21969 pan.el.addClass('carousel-item-' +lr);
21973 cur.el.on('transitionend', function() {
21974 Roo.log("trans end?");
21976 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21977 pan.setActive(true);
21979 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21980 cur.setActive(false);
21982 _this.transition = false;
21984 }, this, { single: true } );
21989 cur.setActive(false);
21990 pan.setActive(true);
21995 showPanelNext : function()
21997 var i = this.indexOfPanel(this.getActivePanel());
21999 if (i >= this.tabs.length - 1 && !this.autoslide) {
22003 if (i >= this.tabs.length - 1 && this.autoslide) {
22007 this.showPanel(this.tabs[i+1]);
22010 showPanelPrev : function()
22012 var i = this.indexOfPanel(this.getActivePanel());
22014 if (i < 1 && !this.autoslide) {
22018 if (i < 1 && this.autoslide) {
22019 i = this.tabs.length;
22022 this.showPanel(this.tabs[i-1]);
22026 addBullet: function()
22028 if(!this.bullets || Roo.isTouch){
22031 var ctr = this.el.select('.carousel-bullets',true).first();
22032 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22033 var bullet = ctr.createChild({
22034 cls : 'bullet bullet-' + i
22035 },ctr.dom.lastChild);
22040 bullet.on('click', (function(e, el, o, ii, t){
22042 e.preventDefault();
22044 this.showPanel(ii);
22046 if(this.autoslide && this.slideFn){
22047 clearInterval(this.slideFn);
22048 this.slideFn = window.setInterval(function() {
22049 _this.showPanelNext();
22053 }).createDelegate(this, [i, bullet], true));
22058 setActiveBullet : function(i)
22064 Roo.each(this.el.select('.bullet', true).elements, function(el){
22065 el.removeClass('selected');
22068 var bullet = this.el.select('.bullet-' + i, true).first();
22074 bullet.addClass('selected');
22085 Roo.apply(Roo.bootstrap.TabGroup, {
22089 * register a Navigation Group
22090 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22092 register : function(navgrp)
22094 this.groups[navgrp.navId] = navgrp;
22098 * fetch a Navigation Group based on the navigation ID
22099 * if one does not exist , it will get created.
22100 * @param {string} the navgroup to add
22101 * @returns {Roo.bootstrap.NavGroup} the navgroup
22103 get: function(navId) {
22104 if (typeof(this.groups[navId]) == 'undefined') {
22105 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22107 return this.groups[navId] ;
22122 * @class Roo.bootstrap.TabPanel
22123 * @extends Roo.bootstrap.Component
22124 * Bootstrap TabPanel class
22125 * @cfg {Boolean} active panel active
22126 * @cfg {String} html panel content
22127 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22128 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22129 * @cfg {String} href click to link..
22130 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22134 * Create a new TabPanel
22135 * @param {Object} config The config object
22138 Roo.bootstrap.TabPanel = function(config){
22139 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22143 * Fires when the active status changes
22144 * @param {Roo.bootstrap.TabPanel} this
22145 * @param {Boolean} state the new state
22150 * @event beforedeactivate
22151 * Fires before a tab is de-activated - can be used to do validation on a form.
22152 * @param {Roo.bootstrap.TabPanel} this
22153 * @return {Boolean} false if there is an error
22156 'beforedeactivate': true
22159 this.tabId = this.tabId || Roo.id();
22163 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22170 touchSlide : false,
22171 getAutoCreate : function(){
22176 // item is needed for carousel - not sure if it has any effect otherwise
22177 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22178 html: this.html || ''
22182 cfg.cls += ' active';
22186 cfg.tabId = this.tabId;
22194 initEvents: function()
22196 var p = this.parent();
22198 this.navId = this.navId || p.navId;
22200 if (typeof(this.navId) != 'undefined') {
22201 // not really needed.. but just in case.. parent should be a NavGroup.
22202 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22206 var i = tg.tabs.length - 1;
22208 if(this.active && tg.bullets > 0 && i < tg.bullets){
22209 tg.setActiveBullet(i);
22213 this.el.on('click', this.onClick, this);
22215 if(Roo.isTouch && this.touchSlide){
22216 this.el.on("touchstart", this.onTouchStart, this);
22217 this.el.on("touchmove", this.onTouchMove, this);
22218 this.el.on("touchend", this.onTouchEnd, this);
22223 onRender : function(ct, position)
22225 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22228 setActive : function(state)
22230 Roo.log("panel - set active " + this.tabId + "=" + state);
22232 this.active = state;
22234 this.el.removeClass('active');
22236 } else if (!this.el.hasClass('active')) {
22237 this.el.addClass('active');
22240 this.fireEvent('changed', this, state);
22243 onClick : function(e)
22245 e.preventDefault();
22247 if(!this.href.length){
22251 window.location.href = this.href;
22260 onTouchStart : function(e)
22262 this.swiping = false;
22264 this.startX = e.browserEvent.touches[0].clientX;
22265 this.startY = e.browserEvent.touches[0].clientY;
22268 onTouchMove : function(e)
22270 this.swiping = true;
22272 this.endX = e.browserEvent.touches[0].clientX;
22273 this.endY = e.browserEvent.touches[0].clientY;
22276 onTouchEnd : function(e)
22283 var tabGroup = this.parent();
22285 if(this.endX > this.startX){ // swiping right
22286 tabGroup.showPanelPrev();
22290 if(this.startX > this.endX){ // swiping left
22291 tabGroup.showPanelNext();
22310 * @class Roo.bootstrap.DateField
22311 * @extends Roo.bootstrap.Input
22312 * Bootstrap DateField class
22313 * @cfg {Number} weekStart default 0
22314 * @cfg {String} viewMode default empty, (months|years)
22315 * @cfg {String} minViewMode default empty, (months|years)
22316 * @cfg {Number} startDate default -Infinity
22317 * @cfg {Number} endDate default Infinity
22318 * @cfg {Boolean} todayHighlight default false
22319 * @cfg {Boolean} todayBtn default false
22320 * @cfg {Boolean} calendarWeeks default false
22321 * @cfg {Object} daysOfWeekDisabled default empty
22322 * @cfg {Boolean} singleMode default false (true | false)
22324 * @cfg {Boolean} keyboardNavigation default true
22325 * @cfg {String} language default en
22328 * Create a new DateField
22329 * @param {Object} config The config object
22332 Roo.bootstrap.DateField = function(config){
22333 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22337 * Fires when this field show.
22338 * @param {Roo.bootstrap.DateField} this
22339 * @param {Mixed} date The date value
22344 * Fires when this field hide.
22345 * @param {Roo.bootstrap.DateField} this
22346 * @param {Mixed} date The date value
22351 * Fires when select a date.
22352 * @param {Roo.bootstrap.DateField} this
22353 * @param {Mixed} date The date value
22357 * @event beforeselect
22358 * Fires when before select a date.
22359 * @param {Roo.bootstrap.DateField} this
22360 * @param {Mixed} date The date value
22362 beforeselect : true
22366 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22369 * @cfg {String} format
22370 * The default date format string which can be overriden for localization support. The format must be
22371 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22375 * @cfg {String} altFormats
22376 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22377 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22379 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22387 todayHighlight : false,
22393 keyboardNavigation: true,
22395 calendarWeeks: false,
22397 startDate: -Infinity,
22401 daysOfWeekDisabled: [],
22405 singleMode : false,
22407 UTCDate: function()
22409 return new Date(Date.UTC.apply(Date, arguments));
22412 UTCToday: function()
22414 var today = new Date();
22415 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22418 getDate: function() {
22419 var d = this.getUTCDate();
22420 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22423 getUTCDate: function() {
22427 setDate: function(d) {
22428 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22431 setUTCDate: function(d) {
22433 this.setValue(this.formatDate(this.date));
22436 onRender: function(ct, position)
22439 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22441 this.language = this.language || 'en';
22442 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22443 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22445 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22446 this.format = this.format || 'm/d/y';
22447 this.isInline = false;
22448 this.isInput = true;
22449 this.component = this.el.select('.add-on', true).first() || false;
22450 this.component = (this.component && this.component.length === 0) ? false : this.component;
22451 this.hasInput = this.component && this.inputEl().length;
22453 if (typeof(this.minViewMode === 'string')) {
22454 switch (this.minViewMode) {
22456 this.minViewMode = 1;
22459 this.minViewMode = 2;
22462 this.minViewMode = 0;
22467 if (typeof(this.viewMode === 'string')) {
22468 switch (this.viewMode) {
22481 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22483 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22485 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22487 this.picker().on('mousedown', this.onMousedown, this);
22488 this.picker().on('click', this.onClick, this);
22490 this.picker().addClass('datepicker-dropdown');
22492 this.startViewMode = this.viewMode;
22494 if(this.singleMode){
22495 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22496 v.setVisibilityMode(Roo.Element.DISPLAY);
22500 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22501 v.setStyle('width', '189px');
22505 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22506 if(!this.calendarWeeks){
22511 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22512 v.attr('colspan', function(i, val){
22513 return parseInt(val) + 1;
22518 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22520 this.setStartDate(this.startDate);
22521 this.setEndDate(this.endDate);
22523 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22530 if(this.isInline) {
22535 picker : function()
22537 return this.pickerEl;
22538 // return this.el.select('.datepicker', true).first();
22541 fillDow: function()
22543 var dowCnt = this.weekStart;
22552 if(this.calendarWeeks){
22560 while (dowCnt < this.weekStart + 7) {
22564 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22568 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22571 fillMonths: function()
22574 var months = this.picker().select('>.datepicker-months td', true).first();
22576 months.dom.innerHTML = '';
22582 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22585 months.createChild(month);
22592 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;
22594 if (this.date < this.startDate) {
22595 this.viewDate = new Date(this.startDate);
22596 } else if (this.date > this.endDate) {
22597 this.viewDate = new Date(this.endDate);
22599 this.viewDate = new Date(this.date);
22607 var d = new Date(this.viewDate),
22608 year = d.getUTCFullYear(),
22609 month = d.getUTCMonth(),
22610 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22611 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22612 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22613 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22614 currentDate = this.date && this.date.valueOf(),
22615 today = this.UTCToday();
22617 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22619 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22621 // this.picker.select('>tfoot th.today').
22622 // .text(dates[this.language].today)
22623 // .toggle(this.todayBtn !== false);
22625 this.updateNavArrows();
22628 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22630 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22632 prevMonth.setUTCDate(day);
22634 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22636 var nextMonth = new Date(prevMonth);
22638 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22640 nextMonth = nextMonth.valueOf();
22642 var fillMonths = false;
22644 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22646 while(prevMonth.valueOf() <= nextMonth) {
22649 if (prevMonth.getUTCDay() === this.weekStart) {
22651 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22659 if(this.calendarWeeks){
22660 // ISO 8601: First week contains first thursday.
22661 // ISO also states week starts on Monday, but we can be more abstract here.
22663 // Start of current week: based on weekstart/current date
22664 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22665 // Thursday of this week
22666 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22667 // First Thursday of year, year from thursday
22668 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22669 // Calendar week: ms between thursdays, div ms per day, div 7 days
22670 calWeek = (th - yth) / 864e5 / 7 + 1;
22672 fillMonths.cn.push({
22680 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22682 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22685 if (this.todayHighlight &&
22686 prevMonth.getUTCFullYear() == today.getFullYear() &&
22687 prevMonth.getUTCMonth() == today.getMonth() &&
22688 prevMonth.getUTCDate() == today.getDate()) {
22689 clsName += ' today';
22692 if (currentDate && prevMonth.valueOf() === currentDate) {
22693 clsName += ' active';
22696 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22697 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22698 clsName += ' disabled';
22701 fillMonths.cn.push({
22703 cls: 'day ' + clsName,
22704 html: prevMonth.getDate()
22707 prevMonth.setDate(prevMonth.getDate()+1);
22710 var currentYear = this.date && this.date.getUTCFullYear();
22711 var currentMonth = this.date && this.date.getUTCMonth();
22713 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22715 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22716 v.removeClass('active');
22718 if(currentYear === year && k === currentMonth){
22719 v.addClass('active');
22722 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22723 v.addClass('disabled');
22729 year = parseInt(year/10, 10) * 10;
22731 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22733 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22736 for (var i = -1; i < 11; i++) {
22737 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22739 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22747 showMode: function(dir)
22750 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22753 Roo.each(this.picker().select('>div',true).elements, function(v){
22754 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22757 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22762 if(this.isInline) {
22766 this.picker().removeClass(['bottom', 'top']);
22768 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22770 * place to the top of element!
22774 this.picker().addClass('top');
22775 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22780 this.picker().addClass('bottom');
22782 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22785 parseDate : function(value)
22787 if(!value || value instanceof Date){
22790 var v = Date.parseDate(value, this.format);
22791 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22792 v = Date.parseDate(value, 'Y-m-d');
22794 if(!v && this.altFormats){
22795 if(!this.altFormatsArray){
22796 this.altFormatsArray = this.altFormats.split("|");
22798 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22799 v = Date.parseDate(value, this.altFormatsArray[i]);
22805 formatDate : function(date, fmt)
22807 return (!date || !(date instanceof Date)) ?
22808 date : date.dateFormat(fmt || this.format);
22811 onFocus : function()
22813 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22817 onBlur : function()
22819 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22821 var d = this.inputEl().getValue();
22828 showPopup : function()
22830 this.picker().show();
22834 this.fireEvent('showpopup', this, this.date);
22837 hidePopup : function()
22839 if(this.isInline) {
22842 this.picker().hide();
22843 this.viewMode = this.startViewMode;
22846 this.fireEvent('hidepopup', this, this.date);
22850 onMousedown: function(e)
22852 e.stopPropagation();
22853 e.preventDefault();
22858 Roo.bootstrap.DateField.superclass.keyup.call(this);
22862 setValue: function(v)
22864 if(this.fireEvent('beforeselect', this, v) !== false){
22865 var d = new Date(this.parseDate(v) ).clearTime();
22867 if(isNaN(d.getTime())){
22868 this.date = this.viewDate = '';
22869 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22873 v = this.formatDate(d);
22875 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22877 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22881 this.fireEvent('select', this, this.date);
22885 getValue: function()
22887 return this.formatDate(this.date);
22890 fireKey: function(e)
22892 if (!this.picker().isVisible()){
22893 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22899 var dateChanged = false,
22901 newDate, newViewDate;
22906 e.preventDefault();
22910 if (!this.keyboardNavigation) {
22913 dir = e.keyCode == 37 ? -1 : 1;
22916 newDate = this.moveYear(this.date, dir);
22917 newViewDate = this.moveYear(this.viewDate, dir);
22918 } else if (e.shiftKey){
22919 newDate = this.moveMonth(this.date, dir);
22920 newViewDate = this.moveMonth(this.viewDate, dir);
22922 newDate = new Date(this.date);
22923 newDate.setUTCDate(this.date.getUTCDate() + dir);
22924 newViewDate = new Date(this.viewDate);
22925 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22927 if (this.dateWithinRange(newDate)){
22928 this.date = newDate;
22929 this.viewDate = newViewDate;
22930 this.setValue(this.formatDate(this.date));
22932 e.preventDefault();
22933 dateChanged = true;
22938 if (!this.keyboardNavigation) {
22941 dir = e.keyCode == 38 ? -1 : 1;
22943 newDate = this.moveYear(this.date, dir);
22944 newViewDate = this.moveYear(this.viewDate, dir);
22945 } else if (e.shiftKey){
22946 newDate = this.moveMonth(this.date, dir);
22947 newViewDate = this.moveMonth(this.viewDate, dir);
22949 newDate = new Date(this.date);
22950 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22951 newViewDate = new Date(this.viewDate);
22952 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22954 if (this.dateWithinRange(newDate)){
22955 this.date = newDate;
22956 this.viewDate = newViewDate;
22957 this.setValue(this.formatDate(this.date));
22959 e.preventDefault();
22960 dateChanged = true;
22964 this.setValue(this.formatDate(this.date));
22966 e.preventDefault();
22969 this.setValue(this.formatDate(this.date));
22983 onClick: function(e)
22985 e.stopPropagation();
22986 e.preventDefault();
22988 var target = e.getTarget();
22990 if(target.nodeName.toLowerCase() === 'i'){
22991 target = Roo.get(target).dom.parentNode;
22994 var nodeName = target.nodeName;
22995 var className = target.className;
22996 var html = target.innerHTML;
22997 //Roo.log(nodeName);
22999 switch(nodeName.toLowerCase()) {
23001 switch(className) {
23007 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23008 switch(this.viewMode){
23010 this.viewDate = this.moveMonth(this.viewDate, dir);
23014 this.viewDate = this.moveYear(this.viewDate, dir);
23020 var date = new Date();
23021 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23023 this.setValue(this.formatDate(this.date));
23030 if (className.indexOf('disabled') < 0) {
23031 if (!this.viewDate) {
23032 this.viewDate = new Date();
23034 this.viewDate.setUTCDate(1);
23035 if (className.indexOf('month') > -1) {
23036 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23038 var year = parseInt(html, 10) || 0;
23039 this.viewDate.setUTCFullYear(year);
23043 if(this.singleMode){
23044 this.setValue(this.formatDate(this.viewDate));
23055 //Roo.log(className);
23056 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23057 var day = parseInt(html, 10) || 1;
23058 var year = (this.viewDate || new Date()).getUTCFullYear(),
23059 month = (this.viewDate || new Date()).getUTCMonth();
23061 if (className.indexOf('old') > -1) {
23068 } else if (className.indexOf('new') > -1) {
23076 //Roo.log([year,month,day]);
23077 this.date = this.UTCDate(year, month, day,0,0,0,0);
23078 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23080 //Roo.log(this.formatDate(this.date));
23081 this.setValue(this.formatDate(this.date));
23088 setStartDate: function(startDate)
23090 this.startDate = startDate || -Infinity;
23091 if (this.startDate !== -Infinity) {
23092 this.startDate = this.parseDate(this.startDate);
23095 this.updateNavArrows();
23098 setEndDate: function(endDate)
23100 this.endDate = endDate || Infinity;
23101 if (this.endDate !== Infinity) {
23102 this.endDate = this.parseDate(this.endDate);
23105 this.updateNavArrows();
23108 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23110 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23111 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23112 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23114 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23115 return parseInt(d, 10);
23118 this.updateNavArrows();
23121 updateNavArrows: function()
23123 if(this.singleMode){
23127 var d = new Date(this.viewDate),
23128 year = d.getUTCFullYear(),
23129 month = d.getUTCMonth();
23131 Roo.each(this.picker().select('.prev', true).elements, function(v){
23133 switch (this.viewMode) {
23136 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23142 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23149 Roo.each(this.picker().select('.next', true).elements, function(v){
23151 switch (this.viewMode) {
23154 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23160 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23168 moveMonth: function(date, dir)
23173 var new_date = new Date(date.valueOf()),
23174 day = new_date.getUTCDate(),
23175 month = new_date.getUTCMonth(),
23176 mag = Math.abs(dir),
23178 dir = dir > 0 ? 1 : -1;
23181 // If going back one month, make sure month is not current month
23182 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23184 return new_date.getUTCMonth() == month;
23186 // If going forward one month, make sure month is as expected
23187 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23189 return new_date.getUTCMonth() != new_month;
23191 new_month = month + dir;
23192 new_date.setUTCMonth(new_month);
23193 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23194 if (new_month < 0 || new_month > 11) {
23195 new_month = (new_month + 12) % 12;
23198 // For magnitudes >1, move one month at a time...
23199 for (var i=0; i<mag; i++) {
23200 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23201 new_date = this.moveMonth(new_date, dir);
23203 // ...then reset the day, keeping it in the new month
23204 new_month = new_date.getUTCMonth();
23205 new_date.setUTCDate(day);
23207 return new_month != new_date.getUTCMonth();
23210 // Common date-resetting loop -- if date is beyond end of month, make it
23213 new_date.setUTCDate(--day);
23214 new_date.setUTCMonth(new_month);
23219 moveYear: function(date, dir)
23221 return this.moveMonth(date, dir*12);
23224 dateWithinRange: function(date)
23226 return date >= this.startDate && date <= this.endDate;
23232 this.picker().remove();
23235 validateValue : function(value)
23237 if(this.getVisibilityEl().hasClass('hidden')){
23241 if(value.length < 1) {
23242 if(this.allowBlank){
23248 if(value.length < this.minLength){
23251 if(value.length > this.maxLength){
23255 var vt = Roo.form.VTypes;
23256 if(!vt[this.vtype](value, this)){
23260 if(typeof this.validator == "function"){
23261 var msg = this.validator(value);
23267 if(this.regex && !this.regex.test(value)){
23271 if(typeof(this.parseDate(value)) == 'undefined'){
23275 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23279 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23289 this.date = this.viewDate = '';
23291 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23296 Roo.apply(Roo.bootstrap.DateField, {
23307 html: '<i class="fa fa-arrow-left"/>'
23317 html: '<i class="fa fa-arrow-right"/>'
23359 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23360 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23361 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23362 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23363 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23376 navFnc: 'FullYear',
23381 navFnc: 'FullYear',
23386 Roo.apply(Roo.bootstrap.DateField, {
23390 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23394 cls: 'datepicker-days',
23398 cls: 'table-condensed',
23400 Roo.bootstrap.DateField.head,
23404 Roo.bootstrap.DateField.footer
23411 cls: 'datepicker-months',
23415 cls: 'table-condensed',
23417 Roo.bootstrap.DateField.head,
23418 Roo.bootstrap.DateField.content,
23419 Roo.bootstrap.DateField.footer
23426 cls: 'datepicker-years',
23430 cls: 'table-condensed',
23432 Roo.bootstrap.DateField.head,
23433 Roo.bootstrap.DateField.content,
23434 Roo.bootstrap.DateField.footer
23453 * @class Roo.bootstrap.TimeField
23454 * @extends Roo.bootstrap.Input
23455 * Bootstrap DateField class
23459 * Create a new TimeField
23460 * @param {Object} config The config object
23463 Roo.bootstrap.TimeField = function(config){
23464 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23468 * Fires when this field show.
23469 * @param {Roo.bootstrap.DateField} thisthis
23470 * @param {Mixed} date The date value
23475 * Fires when this field hide.
23476 * @param {Roo.bootstrap.DateField} this
23477 * @param {Mixed} date The date value
23482 * Fires when select a date.
23483 * @param {Roo.bootstrap.DateField} this
23484 * @param {Mixed} date The date value
23490 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23493 * @cfg {String} format
23494 * The default time format string which can be overriden for localization support. The format must be
23495 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23499 getAutoCreate : function()
23501 this.after = '<i class="fa far fa-clock"></i>';
23502 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23506 onRender: function(ct, position)
23509 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23511 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23513 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23515 this.pop = this.picker().select('>.datepicker-time',true).first();
23516 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23518 this.picker().on('mousedown', this.onMousedown, this);
23519 this.picker().on('click', this.onClick, this);
23521 this.picker().addClass('datepicker-dropdown');
23526 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23527 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23528 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23529 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23530 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23531 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23535 fireKey: function(e){
23536 if (!this.picker().isVisible()){
23537 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23543 e.preventDefault();
23551 this.onTogglePeriod();
23554 this.onIncrementMinutes();
23557 this.onDecrementMinutes();
23566 onClick: function(e) {
23567 e.stopPropagation();
23568 e.preventDefault();
23571 picker : function()
23573 return this.pickerEl;
23576 fillTime: function()
23578 var time = this.pop.select('tbody', true).first();
23580 time.dom.innerHTML = '';
23595 cls: 'hours-up fa fas fa-chevron-up'
23615 cls: 'minutes-up fa fas fa-chevron-up'
23636 cls: 'timepicker-hour',
23651 cls: 'timepicker-minute',
23666 cls: 'btn btn-primary period',
23688 cls: 'hours-down fa fas fa-chevron-down'
23708 cls: 'minutes-down fa fas fa-chevron-down'
23726 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23733 var hours = this.time.getHours();
23734 var minutes = this.time.getMinutes();
23747 hours = hours - 12;
23751 hours = '0' + hours;
23755 minutes = '0' + minutes;
23758 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23759 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23760 this.pop.select('button', true).first().dom.innerHTML = period;
23766 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23768 var cls = ['bottom'];
23770 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23777 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23781 //this.picker().setXY(20000,20000);
23782 this.picker().addClass(cls.join('-'));
23786 Roo.each(cls, function(c){
23791 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23792 //_this.picker().setTop(_this.inputEl().getHeight());
23796 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23798 //_this.picker().setTop(0 - _this.picker().getHeight());
23803 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23807 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23815 onFocus : function()
23817 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23821 onBlur : function()
23823 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23829 this.picker().show();
23834 this.fireEvent('show', this, this.date);
23839 this.picker().hide();
23842 this.fireEvent('hide', this, this.date);
23845 setTime : function()
23848 this.setValue(this.time.format(this.format));
23850 this.fireEvent('select', this, this.date);
23855 onMousedown: function(e){
23856 e.stopPropagation();
23857 e.preventDefault();
23860 onIncrementHours: function()
23862 Roo.log('onIncrementHours');
23863 this.time = this.time.add(Date.HOUR, 1);
23868 onDecrementHours: function()
23870 Roo.log('onDecrementHours');
23871 this.time = this.time.add(Date.HOUR, -1);
23875 onIncrementMinutes: function()
23877 Roo.log('onIncrementMinutes');
23878 this.time = this.time.add(Date.MINUTE, 1);
23882 onDecrementMinutes: function()
23884 Roo.log('onDecrementMinutes');
23885 this.time = this.time.add(Date.MINUTE, -1);
23889 onTogglePeriod: function()
23891 Roo.log('onTogglePeriod');
23892 this.time = this.time.add(Date.HOUR, 12);
23900 Roo.apply(Roo.bootstrap.TimeField, {
23904 cls: 'datepicker dropdown-menu',
23908 cls: 'datepicker-time',
23912 cls: 'table-condensed',
23941 cls: 'btn btn-info ok',
23969 * @class Roo.bootstrap.MonthField
23970 * @extends Roo.bootstrap.Input
23971 * Bootstrap MonthField class
23973 * @cfg {String} language default en
23976 * Create a new MonthField
23977 * @param {Object} config The config object
23980 Roo.bootstrap.MonthField = function(config){
23981 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23986 * Fires when this field show.
23987 * @param {Roo.bootstrap.MonthField} this
23988 * @param {Mixed} date The date value
23993 * Fires when this field hide.
23994 * @param {Roo.bootstrap.MonthField} this
23995 * @param {Mixed} date The date value
24000 * Fires when select a date.
24001 * @param {Roo.bootstrap.MonthField} this
24002 * @param {String} oldvalue The old value
24003 * @param {String} newvalue The new value
24009 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24011 onRender: function(ct, position)
24014 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24016 this.language = this.language || 'en';
24017 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24018 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24020 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24021 this.isInline = false;
24022 this.isInput = true;
24023 this.component = this.el.select('.add-on', true).first() || false;
24024 this.component = (this.component && this.component.length === 0) ? false : this.component;
24025 this.hasInput = this.component && this.inputEL().length;
24027 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24029 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24031 this.picker().on('mousedown', this.onMousedown, this);
24032 this.picker().on('click', this.onClick, this);
24034 this.picker().addClass('datepicker-dropdown');
24036 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24037 v.setStyle('width', '189px');
24044 if(this.isInline) {
24050 setValue: function(v, suppressEvent)
24052 var o = this.getValue();
24054 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24058 if(suppressEvent !== true){
24059 this.fireEvent('select', this, o, v);
24064 getValue: function()
24069 onClick: function(e)
24071 e.stopPropagation();
24072 e.preventDefault();
24074 var target = e.getTarget();
24076 if(target.nodeName.toLowerCase() === 'i'){
24077 target = Roo.get(target).dom.parentNode;
24080 var nodeName = target.nodeName;
24081 var className = target.className;
24082 var html = target.innerHTML;
24084 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24088 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24090 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24096 picker : function()
24098 return this.pickerEl;
24101 fillMonths: function()
24104 var months = this.picker().select('>.datepicker-months td', true).first();
24106 months.dom.innerHTML = '';
24112 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24115 months.createChild(month);
24124 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24125 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24128 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24129 e.removeClass('active');
24131 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24132 e.addClass('active');
24139 if(this.isInline) {
24143 this.picker().removeClass(['bottom', 'top']);
24145 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24147 * place to the top of element!
24151 this.picker().addClass('top');
24152 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24157 this.picker().addClass('bottom');
24159 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24162 onFocus : function()
24164 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24168 onBlur : function()
24170 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24172 var d = this.inputEl().getValue();
24181 this.picker().show();
24182 this.picker().select('>.datepicker-months', true).first().show();
24186 this.fireEvent('show', this, this.date);
24191 if(this.isInline) {
24194 this.picker().hide();
24195 this.fireEvent('hide', this, this.date);
24199 onMousedown: function(e)
24201 e.stopPropagation();
24202 e.preventDefault();
24207 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24211 fireKey: function(e)
24213 if (!this.picker().isVisible()){
24214 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24225 e.preventDefault();
24229 dir = e.keyCode == 37 ? -1 : 1;
24231 this.vIndex = this.vIndex + dir;
24233 if(this.vIndex < 0){
24237 if(this.vIndex > 11){
24241 if(isNaN(this.vIndex)){
24245 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24251 dir = e.keyCode == 38 ? -1 : 1;
24253 this.vIndex = this.vIndex + dir * 4;
24255 if(this.vIndex < 0){
24259 if(this.vIndex > 11){
24263 if(isNaN(this.vIndex)){
24267 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24272 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24273 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24277 e.preventDefault();
24280 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24281 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24297 this.picker().remove();
24302 Roo.apply(Roo.bootstrap.MonthField, {
24321 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24322 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24327 Roo.apply(Roo.bootstrap.MonthField, {
24331 cls: 'datepicker dropdown-menu roo-dynamic',
24335 cls: 'datepicker-months',
24339 cls: 'table-condensed',
24341 Roo.bootstrap.DateField.content
24361 * @class Roo.bootstrap.CheckBox
24362 * @extends Roo.bootstrap.Input
24363 * Bootstrap CheckBox class
24365 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24366 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24367 * @cfg {String} boxLabel The text that appears beside the checkbox
24368 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24369 * @cfg {Boolean} checked initnal the element
24370 * @cfg {Boolean} inline inline the element (default false)
24371 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24372 * @cfg {String} tooltip label tooltip
24375 * Create a new CheckBox
24376 * @param {Object} config The config object
24379 Roo.bootstrap.CheckBox = function(config){
24380 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24385 * Fires when the element is checked or unchecked.
24386 * @param {Roo.bootstrap.CheckBox} this This input
24387 * @param {Boolean} checked The new checked value
24392 * Fires when the element is click.
24393 * @param {Roo.bootstrap.CheckBox} this This input
24400 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24402 inputType: 'checkbox',
24411 // checkbox success does not make any sense really..
24416 getAutoCreate : function()
24418 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24424 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24427 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24433 type : this.inputType,
24434 value : this.inputValue,
24435 cls : 'roo-' + this.inputType, //'form-box',
24436 placeholder : this.placeholder || ''
24440 if(this.inputType != 'radio'){
24444 cls : 'roo-hidden-value',
24445 value : this.checked ? this.inputValue : this.valueOff
24450 if (this.weight) { // Validity check?
24451 cfg.cls += " " + this.inputType + "-" + this.weight;
24454 if (this.disabled) {
24455 input.disabled=true;
24459 input.checked = this.checked;
24464 input.name = this.name;
24466 if(this.inputType != 'radio'){
24467 hidden.name = this.name;
24468 input.name = '_hidden_' + this.name;
24473 input.cls += ' input-' + this.size;
24478 ['xs','sm','md','lg'].map(function(size){
24479 if (settings[size]) {
24480 cfg.cls += ' col-' + size + '-' + settings[size];
24484 var inputblock = input;
24486 if (this.before || this.after) {
24489 cls : 'input-group',
24494 inputblock.cn.push({
24496 cls : 'input-group-addon',
24501 inputblock.cn.push(input);
24503 if(this.inputType != 'radio'){
24504 inputblock.cn.push(hidden);
24508 inputblock.cn.push({
24510 cls : 'input-group-addon',
24516 var boxLabelCfg = false;
24522 //'for': id, // box label is handled by onclick - so no for...
24524 html: this.boxLabel
24527 boxLabelCfg.tooltip = this.tooltip;
24533 if (align ==='left' && this.fieldLabel.length) {
24534 // Roo.log("left and has label");
24539 cls : 'control-label',
24540 html : this.fieldLabel
24551 cfg.cn[1].cn.push(boxLabelCfg);
24554 if(this.labelWidth > 12){
24555 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24558 if(this.labelWidth < 13 && this.labelmd == 0){
24559 this.labelmd = this.labelWidth;
24562 if(this.labellg > 0){
24563 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24564 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24567 if(this.labelmd > 0){
24568 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24569 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24572 if(this.labelsm > 0){
24573 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24574 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24577 if(this.labelxs > 0){
24578 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24579 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24582 } else if ( this.fieldLabel.length) {
24583 // Roo.log(" label");
24587 tag: this.boxLabel ? 'span' : 'label',
24589 cls: 'control-label box-input-label',
24590 //cls : 'input-group-addon',
24591 html : this.fieldLabel
24598 cfg.cn.push(boxLabelCfg);
24603 // Roo.log(" no label && no align");
24604 cfg.cn = [ inputblock ] ;
24606 cfg.cn.push(boxLabelCfg);
24614 if(this.inputType != 'radio'){
24615 cfg.cn.push(hidden);
24623 * return the real input element.
24625 inputEl: function ()
24627 return this.el.select('input.roo-' + this.inputType,true).first();
24629 hiddenEl: function ()
24631 return this.el.select('input.roo-hidden-value',true).first();
24634 labelEl: function()
24636 return this.el.select('label.control-label',true).first();
24638 /* depricated... */
24642 return this.labelEl();
24645 boxLabelEl: function()
24647 return this.el.select('label.box-label',true).first();
24650 initEvents : function()
24652 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24654 this.inputEl().on('click', this.onClick, this);
24656 if (this.boxLabel) {
24657 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24660 this.startValue = this.getValue();
24663 Roo.bootstrap.CheckBox.register(this);
24667 onClick : function(e)
24669 if(this.fireEvent('click', this, e) !== false){
24670 this.setChecked(!this.checked);
24675 setChecked : function(state,suppressEvent)
24677 this.startValue = this.getValue();
24679 if(this.inputType == 'radio'){
24681 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24682 e.dom.checked = false;
24685 this.inputEl().dom.checked = true;
24687 this.inputEl().dom.value = this.inputValue;
24689 if(suppressEvent !== true){
24690 this.fireEvent('check', this, true);
24698 this.checked = state;
24700 this.inputEl().dom.checked = state;
24703 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24705 if(suppressEvent !== true){
24706 this.fireEvent('check', this, state);
24712 getValue : function()
24714 if(this.inputType == 'radio'){
24715 return this.getGroupValue();
24718 return this.hiddenEl().dom.value;
24722 getGroupValue : function()
24724 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24728 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24731 setValue : function(v,suppressEvent)
24733 if(this.inputType == 'radio'){
24734 this.setGroupValue(v, suppressEvent);
24738 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24743 setGroupValue : function(v, suppressEvent)
24745 this.startValue = this.getValue();
24747 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24748 e.dom.checked = false;
24750 if(e.dom.value == v){
24751 e.dom.checked = true;
24755 if(suppressEvent !== true){
24756 this.fireEvent('check', this, true);
24764 validate : function()
24766 if(this.getVisibilityEl().hasClass('hidden')){
24772 (this.inputType == 'radio' && this.validateRadio()) ||
24773 (this.inputType == 'checkbox' && this.validateCheckbox())
24779 this.markInvalid();
24783 validateRadio : function()
24785 if(this.getVisibilityEl().hasClass('hidden')){
24789 if(this.allowBlank){
24795 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24796 if(!e.dom.checked){
24808 validateCheckbox : function()
24811 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24812 //return (this.getValue() == this.inputValue) ? true : false;
24815 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24823 for(var i in group){
24824 if(group[i].el.isVisible(true)){
24832 for(var i in group){
24837 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24844 * Mark this field as valid
24846 markValid : function()
24850 this.fireEvent('valid', this);
24852 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24855 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24862 if(this.inputType == 'radio'){
24863 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24864 var fg = e.findParent('.form-group', false, true);
24865 if (Roo.bootstrap.version == 3) {
24866 fg.removeClass([_this.invalidClass, _this.validClass]);
24867 fg.addClass(_this.validClass);
24869 fg.removeClass(['is-valid', 'is-invalid']);
24870 fg.addClass('is-valid');
24878 var fg = this.el.findParent('.form-group', false, true);
24879 if (Roo.bootstrap.version == 3) {
24880 fg.removeClass([this.invalidClass, this.validClass]);
24881 fg.addClass(this.validClass);
24883 fg.removeClass(['is-valid', 'is-invalid']);
24884 fg.addClass('is-valid');
24889 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24895 for(var i in group){
24896 var fg = group[i].el.findParent('.form-group', false, true);
24897 if (Roo.bootstrap.version == 3) {
24898 fg.removeClass([this.invalidClass, this.validClass]);
24899 fg.addClass(this.validClass);
24901 fg.removeClass(['is-valid', 'is-invalid']);
24902 fg.addClass('is-valid');
24908 * Mark this field as invalid
24909 * @param {String} msg The validation message
24911 markInvalid : function(msg)
24913 if(this.allowBlank){
24919 this.fireEvent('invalid', this, msg);
24921 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24924 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24928 label.markInvalid();
24931 if(this.inputType == 'radio'){
24933 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24934 var fg = e.findParent('.form-group', false, true);
24935 if (Roo.bootstrap.version == 3) {
24936 fg.removeClass([_this.invalidClass, _this.validClass]);
24937 fg.addClass(_this.invalidClass);
24939 fg.removeClass(['is-invalid', 'is-valid']);
24940 fg.addClass('is-invalid');
24948 var fg = this.el.findParent('.form-group', false, true);
24949 if (Roo.bootstrap.version == 3) {
24950 fg.removeClass([_this.invalidClass, _this.validClass]);
24951 fg.addClass(_this.invalidClass);
24953 fg.removeClass(['is-invalid', 'is-valid']);
24954 fg.addClass('is-invalid');
24959 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24965 for(var i in group){
24966 var fg = group[i].el.findParent('.form-group', false, true);
24967 if (Roo.bootstrap.version == 3) {
24968 fg.removeClass([_this.invalidClass, _this.validClass]);
24969 fg.addClass(_this.invalidClass);
24971 fg.removeClass(['is-invalid', 'is-valid']);
24972 fg.addClass('is-invalid');
24978 clearInvalid : function()
24980 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24982 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24984 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24986 if (label && label.iconEl) {
24987 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24988 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24992 disable : function()
24994 if(this.inputType != 'radio'){
24995 Roo.bootstrap.CheckBox.superclass.disable.call(this);
25002 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25003 _this.getActionEl().addClass(this.disabledClass);
25004 e.dom.disabled = true;
25008 this.disabled = true;
25009 this.fireEvent("disable", this);
25013 enable : function()
25015 if(this.inputType != 'radio'){
25016 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25023 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25024 _this.getActionEl().removeClass(this.disabledClass);
25025 e.dom.disabled = false;
25029 this.disabled = false;
25030 this.fireEvent("enable", this);
25034 setBoxLabel : function(v)
25039 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25045 Roo.apply(Roo.bootstrap.CheckBox, {
25050 * register a CheckBox Group
25051 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25053 register : function(checkbox)
25055 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25056 this.groups[checkbox.groupId] = {};
25059 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25063 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25067 * fetch a CheckBox Group based on the group ID
25068 * @param {string} the group ID
25069 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25071 get: function(groupId) {
25072 if (typeof(this.groups[groupId]) == 'undefined') {
25076 return this.groups[groupId] ;
25089 * @class Roo.bootstrap.Radio
25090 * @extends Roo.bootstrap.Component
25091 * Bootstrap Radio class
25092 * @cfg {String} boxLabel - the label associated
25093 * @cfg {String} value - the value of radio
25096 * Create a new Radio
25097 * @param {Object} config The config object
25099 Roo.bootstrap.Radio = function(config){
25100 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25104 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25110 getAutoCreate : function()
25114 cls : 'form-group radio',
25119 html : this.boxLabel
25127 initEvents : function()
25129 this.parent().register(this);
25131 this.el.on('click', this.onClick, this);
25135 onClick : function(e)
25137 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25138 this.setChecked(true);
25142 setChecked : function(state, suppressEvent)
25144 this.parent().setValue(this.value, suppressEvent);
25148 setBoxLabel : function(v)
25153 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25168 * @class Roo.bootstrap.SecurePass
25169 * @extends Roo.bootstrap.Input
25170 * Bootstrap SecurePass class
25174 * Create a new SecurePass
25175 * @param {Object} config The config object
25178 Roo.bootstrap.SecurePass = function (config) {
25179 // these go here, so the translation tool can replace them..
25181 PwdEmpty: "Please type a password, and then retype it to confirm.",
25182 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25183 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25184 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25185 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25186 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25187 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25188 TooWeak: "Your password is Too Weak."
25190 this.meterLabel = "Password strength:";
25191 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25192 this.meterClass = [
25193 "roo-password-meter-tooweak",
25194 "roo-password-meter-weak",
25195 "roo-password-meter-medium",
25196 "roo-password-meter-strong",
25197 "roo-password-meter-grey"
25202 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25205 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25207 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25209 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25210 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25211 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25212 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25213 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25214 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25215 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25225 * @cfg {String/Object} Label for the strength meter (defaults to
25226 * 'Password strength:')
25231 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25232 * ['Weak', 'Medium', 'Strong'])
25235 pwdStrengths: false,
25248 initEvents: function ()
25250 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25252 if (this.el.is('input[type=password]') && Roo.isSafari) {
25253 this.el.on('keydown', this.SafariOnKeyDown, this);
25256 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25259 onRender: function (ct, position)
25261 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25262 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25263 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25265 this.trigger.createChild({
25270 cls: 'roo-password-meter-grey col-xs-12',
25273 //width: this.meterWidth + 'px'
25277 cls: 'roo-password-meter-text'
25283 if (this.hideTrigger) {
25284 this.trigger.setDisplayed(false);
25286 this.setSize(this.width || '', this.height || '');
25289 onDestroy: function ()
25291 if (this.trigger) {
25292 this.trigger.removeAllListeners();
25293 this.trigger.remove();
25296 this.wrap.remove();
25298 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25301 checkStrength: function ()
25303 var pwd = this.inputEl().getValue();
25304 if (pwd == this._lastPwd) {
25309 if (this.ClientSideStrongPassword(pwd)) {
25311 } else if (this.ClientSideMediumPassword(pwd)) {
25313 } else if (this.ClientSideWeakPassword(pwd)) {
25319 Roo.log('strength1: ' + strength);
25321 //var pm = this.trigger.child('div/div/div').dom;
25322 var pm = this.trigger.child('div/div');
25323 pm.removeClass(this.meterClass);
25324 pm.addClass(this.meterClass[strength]);
25327 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25329 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25331 this._lastPwd = pwd;
25335 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25337 this._lastPwd = '';
25339 var pm = this.trigger.child('div/div');
25340 pm.removeClass(this.meterClass);
25341 pm.addClass('roo-password-meter-grey');
25344 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25347 this.inputEl().dom.type='password';
25350 validateValue: function (value)
25352 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25355 if (value.length == 0) {
25356 if (this.allowBlank) {
25357 this.clearInvalid();
25361 this.markInvalid(this.errors.PwdEmpty);
25362 this.errorMsg = this.errors.PwdEmpty;
25370 if (!value.match(/[\x21-\x7e]+/)) {
25371 this.markInvalid(this.errors.PwdBadChar);
25372 this.errorMsg = this.errors.PwdBadChar;
25375 if (value.length < 6) {
25376 this.markInvalid(this.errors.PwdShort);
25377 this.errorMsg = this.errors.PwdShort;
25380 if (value.length > 16) {
25381 this.markInvalid(this.errors.PwdLong);
25382 this.errorMsg = this.errors.PwdLong;
25386 if (this.ClientSideStrongPassword(value)) {
25388 } else if (this.ClientSideMediumPassword(value)) {
25390 } else if (this.ClientSideWeakPassword(value)) {
25397 if (strength < 2) {
25398 //this.markInvalid(this.errors.TooWeak);
25399 this.errorMsg = this.errors.TooWeak;
25404 console.log('strength2: ' + strength);
25406 //var pm = this.trigger.child('div/div/div').dom;
25408 var pm = this.trigger.child('div/div');
25409 pm.removeClass(this.meterClass);
25410 pm.addClass(this.meterClass[strength]);
25412 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25414 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25416 this.errorMsg = '';
25420 CharacterSetChecks: function (type)
25423 this.fResult = false;
25426 isctype: function (character, type)
25429 case this.kCapitalLetter:
25430 if (character >= 'A' && character <= 'Z') {
25435 case this.kSmallLetter:
25436 if (character >= 'a' && character <= 'z') {
25442 if (character >= '0' && character <= '9') {
25447 case this.kPunctuation:
25448 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25459 IsLongEnough: function (pwd, size)
25461 return !(pwd == null || isNaN(size) || pwd.length < size);
25464 SpansEnoughCharacterSets: function (word, nb)
25466 if (!this.IsLongEnough(word, nb))
25471 var characterSetChecks = new Array(
25472 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25473 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25476 for (var index = 0; index < word.length; ++index) {
25477 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25478 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25479 characterSetChecks[nCharSet].fResult = true;
25486 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25487 if (characterSetChecks[nCharSet].fResult) {
25492 if (nCharSets < nb) {
25498 ClientSideStrongPassword: function (pwd)
25500 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25503 ClientSideMediumPassword: function (pwd)
25505 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25508 ClientSideWeakPassword: function (pwd)
25510 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25513 })//<script type="text/javascript">
25516 * Based Ext JS Library 1.1.1
25517 * Copyright(c) 2006-2007, Ext JS, LLC.
25523 * @class Roo.HtmlEditorCore
25524 * @extends Roo.Component
25525 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25527 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25530 Roo.HtmlEditorCore = function(config){
25533 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25538 * @event initialize
25539 * Fires when the editor is fully initialized (including the iframe)
25540 * @param {Roo.HtmlEditorCore} this
25545 * Fires when the editor is first receives the focus. Any insertion must wait
25546 * until after this event.
25547 * @param {Roo.HtmlEditorCore} this
25551 * @event beforesync
25552 * Fires before the textarea is updated with content from the editor iframe. Return false
25553 * to cancel the sync.
25554 * @param {Roo.HtmlEditorCore} this
25555 * @param {String} html
25559 * @event beforepush
25560 * Fires before the iframe editor is updated with content from the textarea. Return false
25561 * to cancel the push.
25562 * @param {Roo.HtmlEditorCore} this
25563 * @param {String} html
25568 * Fires when the textarea is updated with content from the editor iframe.
25569 * @param {Roo.HtmlEditorCore} this
25570 * @param {String} html
25575 * Fires when the iframe editor is updated with content from the textarea.
25576 * @param {Roo.HtmlEditorCore} this
25577 * @param {String} html
25582 * @event editorevent
25583 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25584 * @param {Roo.HtmlEditorCore} this
25590 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25592 // defaults : white / black...
25593 this.applyBlacklists();
25600 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25604 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25610 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25615 * @cfg {Number} height (in pixels)
25619 * @cfg {Number} width (in pixels)
25624 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25627 stylesheets: false,
25630 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25632 allowComments: false,
25636 // private properties
25637 validationEvent : false,
25639 initialized : false,
25641 sourceEditMode : false,
25642 onFocus : Roo.emptyFn,
25644 hideMode:'offsets',
25648 // blacklist + whitelisted elements..
25655 * Protected method that will not generally be called directly. It
25656 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25657 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25659 getDocMarkup : function(){
25663 // inherit styels from page...??
25664 if (this.stylesheets === false) {
25666 Roo.get(document.head).select('style').each(function(node) {
25667 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25670 Roo.get(document.head).select('link').each(function(node) {
25671 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25674 } else if (!this.stylesheets.length) {
25676 st = '<style type="text/css">' +
25677 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25680 for (var i in this.stylesheets) {
25681 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25686 st += '<style type="text/css">' +
25687 'IMG { cursor: pointer } ' +
25690 var cls = 'roo-htmleditor-body';
25692 if(this.bodyCls.length){
25693 cls += ' ' + this.bodyCls;
25696 return '<html><head>' + st +
25697 //<style type="text/css">' +
25698 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25700 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25704 onRender : function(ct, position)
25707 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25708 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25711 this.el.dom.style.border = '0 none';
25712 this.el.dom.setAttribute('tabIndex', -1);
25713 this.el.addClass('x-hidden hide');
25717 if(Roo.isIE){ // fix IE 1px bogus margin
25718 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25722 this.frameId = Roo.id();
25726 var iframe = this.owner.wrap.createChild({
25728 cls: 'form-control', // bootstrap..
25730 name: this.frameId,
25731 frameBorder : 'no',
25732 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25737 this.iframe = iframe.dom;
25739 this.assignDocWin();
25741 this.doc.designMode = 'on';
25744 this.doc.write(this.getDocMarkup());
25748 var task = { // must defer to wait for browser to be ready
25750 //console.log("run task?" + this.doc.readyState);
25751 this.assignDocWin();
25752 if(this.doc.body || this.doc.readyState == 'complete'){
25754 this.doc.designMode="on";
25758 Roo.TaskMgr.stop(task);
25759 this.initEditor.defer(10, this);
25766 Roo.TaskMgr.start(task);
25771 onResize : function(w, h)
25773 Roo.log('resize: ' +w + ',' + h );
25774 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25778 if(typeof w == 'number'){
25780 this.iframe.style.width = w + 'px';
25782 if(typeof h == 'number'){
25784 this.iframe.style.height = h + 'px';
25786 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25793 * Toggles the editor between standard and source edit mode.
25794 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25796 toggleSourceEdit : function(sourceEditMode){
25798 this.sourceEditMode = sourceEditMode === true;
25800 if(this.sourceEditMode){
25802 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25805 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25806 //this.iframe.className = '';
25809 //this.setSize(this.owner.wrap.getSize());
25810 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25817 * Protected method that will not generally be called directly. If you need/want
25818 * custom HTML cleanup, this is the method you should override.
25819 * @param {String} html The HTML to be cleaned
25820 * return {String} The cleaned HTML
25822 cleanHtml : function(html){
25823 html = String(html);
25824 if(html.length > 5){
25825 if(Roo.isSafari){ // strip safari nonsense
25826 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25829 if(html == ' '){
25836 * HTML Editor -> Textarea
25837 * Protected method that will not generally be called directly. Syncs the contents
25838 * of the editor iframe with the textarea.
25840 syncValue : function(){
25841 if(this.initialized){
25842 var bd = (this.doc.body || this.doc.documentElement);
25843 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25844 var html = bd.innerHTML;
25846 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25847 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25849 html = '<div style="'+m[0]+'">' + html + '</div>';
25852 html = this.cleanHtml(html);
25853 // fix up the special chars.. normaly like back quotes in word...
25854 // however we do not want to do this with chinese..
25855 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25857 var cc = match.charCodeAt();
25859 // Get the character value, handling surrogate pairs
25860 if (match.length == 2) {
25861 // It's a surrogate pair, calculate the Unicode code point
25862 var high = match.charCodeAt(0) - 0xD800;
25863 var low = match.charCodeAt(1) - 0xDC00;
25864 cc = (high * 0x400) + low + 0x10000;
25866 (cc >= 0x4E00 && cc < 0xA000 ) ||
25867 (cc >= 0x3400 && cc < 0x4E00 ) ||
25868 (cc >= 0xf900 && cc < 0xfb00 )
25873 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25874 return "&#" + cc + ";";
25881 if(this.owner.fireEvent('beforesync', this, html) !== false){
25882 this.el.dom.value = html;
25883 this.owner.fireEvent('sync', this, html);
25889 * Protected method that will not generally be called directly. Pushes the value of the textarea
25890 * into the iframe editor.
25892 pushValue : function(){
25893 if(this.initialized){
25894 var v = this.el.dom.value.trim();
25896 // if(v.length < 1){
25900 if(this.owner.fireEvent('beforepush', this, v) !== false){
25901 var d = (this.doc.body || this.doc.documentElement);
25903 this.cleanUpPaste();
25904 this.el.dom.value = d.innerHTML;
25905 this.owner.fireEvent('push', this, v);
25911 deferFocus : function(){
25912 this.focus.defer(10, this);
25916 focus : function(){
25917 if(this.win && !this.sourceEditMode){
25924 assignDocWin: function()
25926 var iframe = this.iframe;
25929 this.doc = iframe.contentWindow.document;
25930 this.win = iframe.contentWindow;
25932 // if (!Roo.get(this.frameId)) {
25935 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25936 // this.win = Roo.get(this.frameId).dom.contentWindow;
25938 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25942 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25943 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25948 initEditor : function(){
25949 //console.log("INIT EDITOR");
25950 this.assignDocWin();
25954 this.doc.designMode="on";
25956 this.doc.write(this.getDocMarkup());
25959 var dbody = (this.doc.body || this.doc.documentElement);
25960 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25961 // this copies styles from the containing element into thsi one..
25962 // not sure why we need all of this..
25963 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25965 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25966 //ss['background-attachment'] = 'fixed'; // w3c
25967 dbody.bgProperties = 'fixed'; // ie
25968 //Roo.DomHelper.applyStyles(dbody, ss);
25969 Roo.EventManager.on(this.doc, {
25970 //'mousedown': this.onEditorEvent,
25971 'mouseup': this.onEditorEvent,
25972 'dblclick': this.onEditorEvent,
25973 'click': this.onEditorEvent,
25974 'keyup': this.onEditorEvent,
25979 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25981 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25982 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25984 this.initialized = true;
25986 this.owner.fireEvent('initialize', this);
25991 onDestroy : function(){
25997 //for (var i =0; i < this.toolbars.length;i++) {
25998 // // fixme - ask toolbars for heights?
25999 // this.toolbars[i].onDestroy();
26002 //this.wrap.dom.innerHTML = '';
26003 //this.wrap.remove();
26008 onFirstFocus : function(){
26010 this.assignDocWin();
26013 this.activated = true;
26016 if(Roo.isGecko){ // prevent silly gecko errors
26018 var s = this.win.getSelection();
26019 if(!s.focusNode || s.focusNode.nodeType != 3){
26020 var r = s.getRangeAt(0);
26021 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26026 this.execCmd('useCSS', true);
26027 this.execCmd('styleWithCSS', false);
26030 this.owner.fireEvent('activate', this);
26034 adjustFont: function(btn){
26035 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26036 //if(Roo.isSafari){ // safari
26039 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26040 if(Roo.isSafari){ // safari
26041 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26042 v = (v < 10) ? 10 : v;
26043 v = (v > 48) ? 48 : v;
26044 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26049 v = Math.max(1, v+adjust);
26051 this.execCmd('FontSize', v );
26054 onEditorEvent : function(e)
26056 this.owner.fireEvent('editorevent', this, e);
26057 // this.updateToolbar();
26058 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26061 insertTag : function(tg)
26063 // could be a bit smarter... -> wrap the current selected tRoo..
26064 if (tg.toLowerCase() == 'span' ||
26065 tg.toLowerCase() == 'code' ||
26066 tg.toLowerCase() == 'sup' ||
26067 tg.toLowerCase() == 'sub'
26070 range = this.createRange(this.getSelection());
26071 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26072 wrappingNode.appendChild(range.extractContents());
26073 range.insertNode(wrappingNode);
26080 this.execCmd("formatblock", tg);
26084 insertText : function(txt)
26088 var range = this.createRange();
26089 range.deleteContents();
26090 //alert(Sender.getAttribute('label'));
26092 range.insertNode(this.doc.createTextNode(txt));
26098 * Executes a Midas editor command on the editor document and performs necessary focus and
26099 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26100 * @param {String} cmd The Midas command
26101 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26103 relayCmd : function(cmd, value){
26105 this.execCmd(cmd, value);
26106 this.owner.fireEvent('editorevent', this);
26107 //this.updateToolbar();
26108 this.owner.deferFocus();
26112 * Executes a Midas editor command directly on the editor document.
26113 * For visual commands, you should use {@link #relayCmd} instead.
26114 * <b>This should only be called after the editor is initialized.</b>
26115 * @param {String} cmd The Midas command
26116 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26118 execCmd : function(cmd, value){
26119 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26126 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26128 * @param {String} text | dom node..
26130 insertAtCursor : function(text)
26133 if(!this.activated){
26139 var r = this.doc.selection.createRange();
26150 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26154 // from jquery ui (MIT licenced)
26156 var win = this.win;
26158 if (win.getSelection && win.getSelection().getRangeAt) {
26159 range = win.getSelection().getRangeAt(0);
26160 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26161 range.insertNode(node);
26162 } else if (win.document.selection && win.document.selection.createRange) {
26163 // no firefox support
26164 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26165 win.document.selection.createRange().pasteHTML(txt);
26167 // no firefox support
26168 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26169 this.execCmd('InsertHTML', txt);
26178 mozKeyPress : function(e){
26180 var c = e.getCharCode(), cmd;
26183 c = String.fromCharCode(c).toLowerCase();
26197 this.cleanUpPaste.defer(100, this);
26205 e.preventDefault();
26213 fixKeys : function(){ // load time branching for fastest keydown performance
26215 return function(e){
26216 var k = e.getKey(), r;
26219 r = this.doc.selection.createRange();
26222 r.pasteHTML('    ');
26229 r = this.doc.selection.createRange();
26231 var target = r.parentElement();
26232 if(!target || target.tagName.toLowerCase() != 'li'){
26234 r.pasteHTML('<br />');
26240 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26241 this.cleanUpPaste.defer(100, this);
26247 }else if(Roo.isOpera){
26248 return function(e){
26249 var k = e.getKey();
26253 this.execCmd('InsertHTML','    ');
26256 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26257 this.cleanUpPaste.defer(100, this);
26262 }else if(Roo.isSafari){
26263 return function(e){
26264 var k = e.getKey();
26268 this.execCmd('InsertText','\t');
26272 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26273 this.cleanUpPaste.defer(100, this);
26281 getAllAncestors: function()
26283 var p = this.getSelectedNode();
26286 a.push(p); // push blank onto stack..
26287 p = this.getParentElement();
26291 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26295 a.push(this.doc.body);
26299 lastSelNode : false,
26302 getSelection : function()
26304 this.assignDocWin();
26305 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26308 getSelectedNode: function()
26310 // this may only work on Gecko!!!
26312 // should we cache this!!!!
26317 var range = this.createRange(this.getSelection()).cloneRange();
26320 var parent = range.parentElement();
26322 var testRange = range.duplicate();
26323 testRange.moveToElementText(parent);
26324 if (testRange.inRange(range)) {
26327 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26330 parent = parent.parentElement;
26335 // is ancestor a text element.
26336 var ac = range.commonAncestorContainer;
26337 if (ac.nodeType == 3) {
26338 ac = ac.parentNode;
26341 var ar = ac.childNodes;
26344 var other_nodes = [];
26345 var has_other_nodes = false;
26346 for (var i=0;i<ar.length;i++) {
26347 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26350 // fullly contained node.
26352 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26357 // probably selected..
26358 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26359 other_nodes.push(ar[i]);
26363 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26368 has_other_nodes = true;
26370 if (!nodes.length && other_nodes.length) {
26371 nodes= other_nodes;
26373 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26379 createRange: function(sel)
26381 // this has strange effects when using with
26382 // top toolbar - not sure if it's a great idea.
26383 //this.editor.contentWindow.focus();
26384 if (typeof sel != "undefined") {
26386 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26388 return this.doc.createRange();
26391 return this.doc.createRange();
26394 getParentElement: function()
26397 this.assignDocWin();
26398 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26400 var range = this.createRange(sel);
26403 var p = range.commonAncestorContainer;
26404 while (p.nodeType == 3) { // text node
26415 * Range intersection.. the hard stuff...
26419 * [ -- selected range --- ]
26423 * if end is before start or hits it. fail.
26424 * if start is after end or hits it fail.
26426 * if either hits (but other is outside. - then it's not
26432 // @see http://www.thismuchiknow.co.uk/?p=64.
26433 rangeIntersectsNode : function(range, node)
26435 var nodeRange = node.ownerDocument.createRange();
26437 nodeRange.selectNode(node);
26439 nodeRange.selectNodeContents(node);
26442 var rangeStartRange = range.cloneRange();
26443 rangeStartRange.collapse(true);
26445 var rangeEndRange = range.cloneRange();
26446 rangeEndRange.collapse(false);
26448 var nodeStartRange = nodeRange.cloneRange();
26449 nodeStartRange.collapse(true);
26451 var nodeEndRange = nodeRange.cloneRange();
26452 nodeEndRange.collapse(false);
26454 return rangeStartRange.compareBoundaryPoints(
26455 Range.START_TO_START, nodeEndRange) == -1 &&
26456 rangeEndRange.compareBoundaryPoints(
26457 Range.START_TO_START, nodeStartRange) == 1;
26461 rangeCompareNode : function(range, node)
26463 var nodeRange = node.ownerDocument.createRange();
26465 nodeRange.selectNode(node);
26467 nodeRange.selectNodeContents(node);
26471 range.collapse(true);
26473 nodeRange.collapse(true);
26475 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26476 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26478 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26480 var nodeIsBefore = ss == 1;
26481 var nodeIsAfter = ee == -1;
26483 if (nodeIsBefore && nodeIsAfter) {
26486 if (!nodeIsBefore && nodeIsAfter) {
26487 return 1; //right trailed.
26490 if (nodeIsBefore && !nodeIsAfter) {
26491 return 2; // left trailed.
26497 // private? - in a new class?
26498 cleanUpPaste : function()
26500 // cleans up the whole document..
26501 Roo.log('cleanuppaste');
26503 this.cleanUpChildren(this.doc.body);
26504 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26505 if (clean != this.doc.body.innerHTML) {
26506 this.doc.body.innerHTML = clean;
26511 cleanWordChars : function(input) {// change the chars to hex code
26512 var he = Roo.HtmlEditorCore;
26514 var output = input;
26515 Roo.each(he.swapCodes, function(sw) {
26516 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26518 output = output.replace(swapper, sw[1]);
26525 cleanUpChildren : function (n)
26527 if (!n.childNodes.length) {
26530 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26531 this.cleanUpChild(n.childNodes[i]);
26538 cleanUpChild : function (node)
26541 //console.log(node);
26542 if (node.nodeName == "#text") {
26543 // clean up silly Windows -- stuff?
26546 if (node.nodeName == "#comment") {
26547 if (!this.allowComments) {
26548 node.parentNode.removeChild(node);
26550 // clean up silly Windows -- stuff?
26553 var lcname = node.tagName.toLowerCase();
26554 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26555 // whitelist of tags..
26557 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26559 node.parentNode.removeChild(node);
26564 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26566 // spans with no attributes - just remove them..
26567 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26568 remove_keep_children = true;
26571 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26572 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26574 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26575 // remove_keep_children = true;
26578 if (remove_keep_children) {
26579 this.cleanUpChildren(node);
26580 // inserts everything just before this node...
26581 while (node.childNodes.length) {
26582 var cn = node.childNodes[0];
26583 node.removeChild(cn);
26584 node.parentNode.insertBefore(cn, node);
26586 node.parentNode.removeChild(node);
26590 if (!node.attributes || !node.attributes.length) {
26595 this.cleanUpChildren(node);
26599 function cleanAttr(n,v)
26602 if (v.match(/^\./) || v.match(/^\//)) {
26605 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26608 if (v.match(/^#/)) {
26611 if (v.match(/^\{/)) { // allow template editing.
26614 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26615 node.removeAttribute(n);
26619 var cwhite = this.cwhite;
26620 var cblack = this.cblack;
26622 function cleanStyle(n,v)
26624 if (v.match(/expression/)) { //XSS?? should we even bother..
26625 node.removeAttribute(n);
26629 var parts = v.split(/;/);
26632 Roo.each(parts, function(p) {
26633 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26637 var l = p.split(':').shift().replace(/\s+/g,'');
26638 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26640 if ( cwhite.length && cblack.indexOf(l) > -1) {
26641 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26642 //node.removeAttribute(n);
26646 // only allow 'c whitelisted system attributes'
26647 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26648 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26649 //node.removeAttribute(n);
26659 if (clean.length) {
26660 node.setAttribute(n, clean.join(';'));
26662 node.removeAttribute(n);
26668 for (var i = node.attributes.length-1; i > -1 ; i--) {
26669 var a = node.attributes[i];
26672 if (a.name.toLowerCase().substr(0,2)=='on') {
26673 node.removeAttribute(a.name);
26676 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26677 node.removeAttribute(a.name);
26680 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26681 cleanAttr(a.name,a.value); // fixme..
26684 if (a.name == 'style') {
26685 cleanStyle(a.name,a.value);
26688 /// clean up MS crap..
26689 // tecnically this should be a list of valid class'es..
26692 if (a.name == 'class') {
26693 if (a.value.match(/^Mso/)) {
26694 node.removeAttribute('class');
26697 if (a.value.match(/^body$/)) {
26698 node.removeAttribute('class');
26709 this.cleanUpChildren(node);
26715 * Clean up MS wordisms...
26717 cleanWord : function(node)
26720 this.cleanWord(this.doc.body);
26725 node.nodeName == 'SPAN' &&
26726 !node.hasAttributes() &&
26727 node.childNodes.length == 1 &&
26728 node.firstChild.nodeName == "#text"
26730 var textNode = node.firstChild;
26731 node.removeChild(textNode);
26732 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26733 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26735 node.parentNode.insertBefore(textNode, node);
26736 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26737 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26739 node.parentNode.removeChild(node);
26742 if (node.nodeName == "#text") {
26743 // clean up silly Windows -- stuff?
26746 if (node.nodeName == "#comment") {
26747 node.parentNode.removeChild(node);
26748 // clean up silly Windows -- stuff?
26752 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26753 node.parentNode.removeChild(node);
26756 //Roo.log(node.tagName);
26757 // remove - but keep children..
26758 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26759 //Roo.log('-- removed');
26760 while (node.childNodes.length) {
26761 var cn = node.childNodes[0];
26762 node.removeChild(cn);
26763 node.parentNode.insertBefore(cn, node);
26764 // move node to parent - and clean it..
26765 this.cleanWord(cn);
26767 node.parentNode.removeChild(node);
26768 /// no need to iterate chidlren = it's got none..
26769 //this.iterateChildren(node, this.cleanWord);
26773 if (node.className.length) {
26775 var cn = node.className.split(/\W+/);
26777 Roo.each(cn, function(cls) {
26778 if (cls.match(/Mso[a-zA-Z]+/)) {
26783 node.className = cna.length ? cna.join(' ') : '';
26785 node.removeAttribute("class");
26789 if (node.hasAttribute("lang")) {
26790 node.removeAttribute("lang");
26793 if (node.hasAttribute("style")) {
26795 var styles = node.getAttribute("style").split(";");
26797 Roo.each(styles, function(s) {
26798 if (!s.match(/:/)) {
26801 var kv = s.split(":");
26802 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26805 // what ever is left... we allow.
26808 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26809 if (!nstyle.length) {
26810 node.removeAttribute('style');
26813 this.iterateChildren(node, this.cleanWord);
26819 * iterateChildren of a Node, calling fn each time, using this as the scole..
26820 * @param {DomNode} node node to iterate children of.
26821 * @param {Function} fn method of this class to call on each item.
26823 iterateChildren : function(node, fn)
26825 if (!node.childNodes.length) {
26828 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26829 fn.call(this, node.childNodes[i])
26835 * cleanTableWidths.
26837 * Quite often pasting from word etc.. results in tables with column and widths.
26838 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26841 cleanTableWidths : function(node)
26846 this.cleanTableWidths(this.doc.body);
26851 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26854 Roo.log(node.tagName);
26855 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26856 this.iterateChildren(node, this.cleanTableWidths);
26859 if (node.hasAttribute('width')) {
26860 node.removeAttribute('width');
26864 if (node.hasAttribute("style")) {
26867 var styles = node.getAttribute("style").split(";");
26869 Roo.each(styles, function(s) {
26870 if (!s.match(/:/)) {
26873 var kv = s.split(":");
26874 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26877 // what ever is left... we allow.
26880 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26881 if (!nstyle.length) {
26882 node.removeAttribute('style');
26886 this.iterateChildren(node, this.cleanTableWidths);
26894 domToHTML : function(currentElement, depth, nopadtext) {
26896 depth = depth || 0;
26897 nopadtext = nopadtext || false;
26899 if (!currentElement) {
26900 return this.domToHTML(this.doc.body);
26903 //Roo.log(currentElement);
26905 var allText = false;
26906 var nodeName = currentElement.nodeName;
26907 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26909 if (nodeName == '#text') {
26911 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26916 if (nodeName != 'BODY') {
26919 // Prints the node tagName, such as <A>, <IMG>, etc
26922 for(i = 0; i < currentElement.attributes.length;i++) {
26924 var aname = currentElement.attributes.item(i).name;
26925 if (!currentElement.attributes.item(i).value.length) {
26928 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26931 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26940 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26943 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26948 // Traverse the tree
26950 var currentElementChild = currentElement.childNodes.item(i);
26951 var allText = true;
26952 var innerHTML = '';
26954 while (currentElementChild) {
26955 // Formatting code (indent the tree so it looks nice on the screen)
26956 var nopad = nopadtext;
26957 if (lastnode == 'SPAN') {
26961 if (currentElementChild.nodeName == '#text') {
26962 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26963 toadd = nopadtext ? toadd : toadd.trim();
26964 if (!nopad && toadd.length > 80) {
26965 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26967 innerHTML += toadd;
26970 currentElementChild = currentElement.childNodes.item(i);
26976 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26978 // Recursively traverse the tree structure of the child node
26979 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26980 lastnode = currentElementChild.nodeName;
26982 currentElementChild=currentElement.childNodes.item(i);
26988 // The remaining code is mostly for formatting the tree
26989 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26994 ret+= "</"+tagName+">";
27000 applyBlacklists : function()
27002 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27003 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27007 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27008 if (b.indexOf(tag) > -1) {
27011 this.white.push(tag);
27015 Roo.each(w, function(tag) {
27016 if (b.indexOf(tag) > -1) {
27019 if (this.white.indexOf(tag) > -1) {
27022 this.white.push(tag);
27027 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27028 if (w.indexOf(tag) > -1) {
27031 this.black.push(tag);
27035 Roo.each(b, function(tag) {
27036 if (w.indexOf(tag) > -1) {
27039 if (this.black.indexOf(tag) > -1) {
27042 this.black.push(tag);
27047 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27048 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27052 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27053 if (b.indexOf(tag) > -1) {
27056 this.cwhite.push(tag);
27060 Roo.each(w, function(tag) {
27061 if (b.indexOf(tag) > -1) {
27064 if (this.cwhite.indexOf(tag) > -1) {
27067 this.cwhite.push(tag);
27072 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27073 if (w.indexOf(tag) > -1) {
27076 this.cblack.push(tag);
27080 Roo.each(b, function(tag) {
27081 if (w.indexOf(tag) > -1) {
27084 if (this.cblack.indexOf(tag) > -1) {
27087 this.cblack.push(tag);
27092 setStylesheets : function(stylesheets)
27094 if(typeof(stylesheets) == 'string'){
27095 Roo.get(this.iframe.contentDocument.head).createChild({
27097 rel : 'stylesheet',
27106 Roo.each(stylesheets, function(s) {
27111 Roo.get(_this.iframe.contentDocument.head).createChild({
27113 rel : 'stylesheet',
27122 removeStylesheets : function()
27126 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27131 setStyle : function(style)
27133 Roo.get(this.iframe.contentDocument.head).createChild({
27142 // hide stuff that is not compatible
27156 * @event specialkey
27160 * @cfg {String} fieldClass @hide
27163 * @cfg {String} focusClass @hide
27166 * @cfg {String} autoCreate @hide
27169 * @cfg {String} inputType @hide
27172 * @cfg {String} invalidClass @hide
27175 * @cfg {String} invalidText @hide
27178 * @cfg {String} msgFx @hide
27181 * @cfg {String} validateOnBlur @hide
27185 Roo.HtmlEditorCore.white = [
27186 'area', 'br', 'img', 'input', 'hr', 'wbr',
27188 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27189 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27190 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27191 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27192 'table', 'ul', 'xmp',
27194 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27197 'dir', 'menu', 'ol', 'ul', 'dl',
27203 Roo.HtmlEditorCore.black = [
27204 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27206 'base', 'basefont', 'bgsound', 'blink', 'body',
27207 'frame', 'frameset', 'head', 'html', 'ilayer',
27208 'iframe', 'layer', 'link', 'meta', 'object',
27209 'script', 'style' ,'title', 'xml' // clean later..
27211 Roo.HtmlEditorCore.clean = [
27212 'script', 'style', 'title', 'xml'
27214 Roo.HtmlEditorCore.remove = [
27219 Roo.HtmlEditorCore.ablack = [
27223 Roo.HtmlEditorCore.aclean = [
27224 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27228 Roo.HtmlEditorCore.pwhite= [
27229 'http', 'https', 'mailto'
27232 // white listed style attributes.
27233 Roo.HtmlEditorCore.cwhite= [
27234 // 'text-align', /// default is to allow most things..
27240 // black listed style attributes.
27241 Roo.HtmlEditorCore.cblack= [
27242 // 'font-size' -- this can be set by the project
27246 Roo.HtmlEditorCore.swapCodes =[
27247 [ 8211, "–" ],
27248 [ 8212, "—" ],
27265 * @class Roo.bootstrap.HtmlEditor
27266 * @extends Roo.bootstrap.TextArea
27267 * Bootstrap HtmlEditor class
27270 * Create a new HtmlEditor
27271 * @param {Object} config The config object
27274 Roo.bootstrap.HtmlEditor = function(config){
27275 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27276 if (!this.toolbars) {
27277 this.toolbars = [];
27280 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27283 * @event initialize
27284 * Fires when the editor is fully initialized (including the iframe)
27285 * @param {HtmlEditor} this
27290 * Fires when the editor is first receives the focus. Any insertion must wait
27291 * until after this event.
27292 * @param {HtmlEditor} this
27296 * @event beforesync
27297 * Fires before the textarea is updated with content from the editor iframe. Return false
27298 * to cancel the sync.
27299 * @param {HtmlEditor} this
27300 * @param {String} html
27304 * @event beforepush
27305 * Fires before the iframe editor is updated with content from the textarea. Return false
27306 * to cancel the push.
27307 * @param {HtmlEditor} this
27308 * @param {String} html
27313 * Fires when the textarea is updated with content from the editor iframe.
27314 * @param {HtmlEditor} this
27315 * @param {String} html
27320 * Fires when the iframe editor is updated with content from the textarea.
27321 * @param {HtmlEditor} this
27322 * @param {String} html
27326 * @event editmodechange
27327 * Fires when the editor switches edit modes
27328 * @param {HtmlEditor} this
27329 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27331 editmodechange: true,
27333 * @event editorevent
27334 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27335 * @param {HtmlEditor} this
27339 * @event firstfocus
27340 * Fires when on first focus - needed by toolbars..
27341 * @param {HtmlEditor} this
27346 * Auto save the htmlEditor value as a file into Events
27347 * @param {HtmlEditor} this
27351 * @event savedpreview
27352 * preview the saved version of htmlEditor
27353 * @param {HtmlEditor} this
27360 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27364 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27369 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27374 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27379 * @cfg {Number} height (in pixels)
27383 * @cfg {Number} width (in pixels)
27388 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27391 stylesheets: false,
27396 // private properties
27397 validationEvent : false,
27399 initialized : false,
27402 onFocus : Roo.emptyFn,
27404 hideMode:'offsets',
27406 tbContainer : false,
27410 toolbarContainer :function() {
27411 return this.wrap.select('.x-html-editor-tb',true).first();
27415 * Protected method that will not generally be called directly. It
27416 * is called when the editor creates its toolbar. Override this method if you need to
27417 * add custom toolbar buttons.
27418 * @param {HtmlEditor} editor
27420 createToolbar : function(){
27421 Roo.log('renewing');
27422 Roo.log("create toolbars");
27424 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27425 this.toolbars[0].render(this.toolbarContainer());
27429 // if (!editor.toolbars || !editor.toolbars.length) {
27430 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27433 // for (var i =0 ; i < editor.toolbars.length;i++) {
27434 // editor.toolbars[i] = Roo.factory(
27435 // typeof(editor.toolbars[i]) == 'string' ?
27436 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27437 // Roo.bootstrap.HtmlEditor);
27438 // editor.toolbars[i].init(editor);
27444 onRender : function(ct, position)
27446 // Roo.log("Call onRender: " + this.xtype);
27448 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27450 this.wrap = this.inputEl().wrap({
27451 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27454 this.editorcore.onRender(ct, position);
27456 if (this.resizable) {
27457 this.resizeEl = new Roo.Resizable(this.wrap, {
27461 minHeight : this.height,
27462 height: this.height,
27463 handles : this.resizable,
27466 resize : function(r, w, h) {
27467 _t.onResize(w,h); // -something
27473 this.createToolbar(this);
27476 if(!this.width && this.resizable){
27477 this.setSize(this.wrap.getSize());
27479 if (this.resizeEl) {
27480 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27481 // should trigger onReize..
27487 onResize : function(w, h)
27489 Roo.log('resize: ' +w + ',' + h );
27490 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27494 if(this.inputEl() ){
27495 if(typeof w == 'number'){
27496 var aw = w - this.wrap.getFrameWidth('lr');
27497 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27500 if(typeof h == 'number'){
27501 var tbh = -11; // fixme it needs to tool bar size!
27502 for (var i =0; i < this.toolbars.length;i++) {
27503 // fixme - ask toolbars for heights?
27504 tbh += this.toolbars[i].el.getHeight();
27505 //if (this.toolbars[i].footer) {
27506 // tbh += this.toolbars[i].footer.el.getHeight();
27514 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27515 ah -= 5; // knock a few pixes off for look..
27516 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27520 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27521 this.editorcore.onResize(ew,eh);
27526 * Toggles the editor between standard and source edit mode.
27527 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27529 toggleSourceEdit : function(sourceEditMode)
27531 this.editorcore.toggleSourceEdit(sourceEditMode);
27533 if(this.editorcore.sourceEditMode){
27534 Roo.log('editor - showing textarea');
27537 // Roo.log(this.syncValue());
27539 this.inputEl().removeClass(['hide', 'x-hidden']);
27540 this.inputEl().dom.removeAttribute('tabIndex');
27541 this.inputEl().focus();
27543 Roo.log('editor - hiding textarea');
27545 // Roo.log(this.pushValue());
27548 this.inputEl().addClass(['hide', 'x-hidden']);
27549 this.inputEl().dom.setAttribute('tabIndex', -1);
27550 //this.deferFocus();
27553 if(this.resizable){
27554 this.setSize(this.wrap.getSize());
27557 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27560 // private (for BoxComponent)
27561 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27563 // private (for BoxComponent)
27564 getResizeEl : function(){
27568 // private (for BoxComponent)
27569 getPositionEl : function(){
27574 initEvents : function(){
27575 this.originalValue = this.getValue();
27579 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27582 // markInvalid : Roo.emptyFn,
27584 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27587 // clearInvalid : Roo.emptyFn,
27589 setValue : function(v){
27590 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27591 this.editorcore.pushValue();
27596 deferFocus : function(){
27597 this.focus.defer(10, this);
27601 focus : function(){
27602 this.editorcore.focus();
27608 onDestroy : function(){
27614 for (var i =0; i < this.toolbars.length;i++) {
27615 // fixme - ask toolbars for heights?
27616 this.toolbars[i].onDestroy();
27619 this.wrap.dom.innerHTML = '';
27620 this.wrap.remove();
27625 onFirstFocus : function(){
27626 //Roo.log("onFirstFocus");
27627 this.editorcore.onFirstFocus();
27628 for (var i =0; i < this.toolbars.length;i++) {
27629 this.toolbars[i].onFirstFocus();
27635 syncValue : function()
27637 this.editorcore.syncValue();
27640 pushValue : function()
27642 this.editorcore.pushValue();
27646 // hide stuff that is not compatible
27660 * @event specialkey
27664 * @cfg {String} fieldClass @hide
27667 * @cfg {String} focusClass @hide
27670 * @cfg {String} autoCreate @hide
27673 * @cfg {String} inputType @hide
27677 * @cfg {String} invalidText @hide
27680 * @cfg {String} msgFx @hide
27683 * @cfg {String} validateOnBlur @hide
27692 Roo.namespace('Roo.bootstrap.htmleditor');
27694 * @class Roo.bootstrap.HtmlEditorToolbar1
27700 new Roo.bootstrap.HtmlEditor({
27703 new Roo.bootstrap.HtmlEditorToolbar1({
27704 disable : { fonts: 1 , format: 1, ..., ... , ...],
27710 * @cfg {Object} disable List of elements to disable..
27711 * @cfg {Array} btns List of additional buttons.
27715 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27718 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27721 Roo.apply(this, config);
27723 // default disabled, based on 'good practice'..
27724 this.disable = this.disable || {};
27725 Roo.applyIf(this.disable, {
27728 specialElements : true
27730 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27732 this.editor = config.editor;
27733 this.editorcore = config.editor.editorcore;
27735 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27737 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27738 // dont call parent... till later.
27740 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27745 editorcore : false,
27750 "h1","h2","h3","h4","h5","h6",
27752 "abbr", "acronym", "address", "cite", "samp", "var",
27756 onRender : function(ct, position)
27758 // Roo.log("Call onRender: " + this.xtype);
27760 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27762 this.el.dom.style.marginBottom = '0';
27764 var editorcore = this.editorcore;
27765 var editor= this.editor;
27768 var btn = function(id,cmd , toggle, handler, html){
27770 var event = toggle ? 'toggle' : 'click';
27775 xns: Roo.bootstrap,
27779 enableToggle:toggle !== false,
27781 pressed : toggle ? false : null,
27784 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27785 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27791 // var cb_box = function...
27796 xns: Roo.bootstrap,
27801 xns: Roo.bootstrap,
27805 Roo.each(this.formats, function(f) {
27806 style.menu.items.push({
27808 xns: Roo.bootstrap,
27809 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27814 editorcore.insertTag(this.tagname);
27821 children.push(style);
27823 btn('bold',false,true);
27824 btn('italic',false,true);
27825 btn('align-left', 'justifyleft',true);
27826 btn('align-center', 'justifycenter',true);
27827 btn('align-right' , 'justifyright',true);
27828 btn('link', false, false, function(btn) {
27829 //Roo.log("create link?");
27830 var url = prompt(this.createLinkText, this.defaultLinkValue);
27831 if(url && url != 'http:/'+'/'){
27832 this.editorcore.relayCmd('createlink', url);
27835 btn('list','insertunorderedlist',true);
27836 btn('pencil', false,true, function(btn){
27838 this.toggleSourceEdit(btn.pressed);
27841 if (this.editor.btns.length > 0) {
27842 for (var i = 0; i<this.editor.btns.length; i++) {
27843 children.push(this.editor.btns[i]);
27851 xns: Roo.bootstrap,
27856 xns: Roo.bootstrap,
27861 cog.menu.items.push({
27863 xns: Roo.bootstrap,
27864 html : Clean styles,
27869 editorcore.insertTag(this.tagname);
27878 this.xtype = 'NavSimplebar';
27880 for(var i=0;i< children.length;i++) {
27882 this.buttons.add(this.addxtypeChild(children[i]));
27886 editor.on('editorevent', this.updateToolbar, this);
27888 onBtnClick : function(id)
27890 this.editorcore.relayCmd(id);
27891 this.editorcore.focus();
27895 * Protected method that will not generally be called directly. It triggers
27896 * a toolbar update by reading the markup state of the current selection in the editor.
27898 updateToolbar: function(){
27900 if(!this.editorcore.activated){
27901 this.editor.onFirstFocus(); // is this neeed?
27905 var btns = this.buttons;
27906 var doc = this.editorcore.doc;
27907 btns.get('bold').setActive(doc.queryCommandState('bold'));
27908 btns.get('italic').setActive(doc.queryCommandState('italic'));
27909 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27911 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27912 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27913 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27915 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27916 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27919 var ans = this.editorcore.getAllAncestors();
27920 if (this.formatCombo) {
27923 var store = this.formatCombo.store;
27924 this.formatCombo.setValue("");
27925 for (var i =0; i < ans.length;i++) {
27926 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27928 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27936 // hides menus... - so this cant be on a menu...
27937 Roo.bootstrap.MenuMgr.hideAll();
27939 Roo.bootstrap.MenuMgr.hideAll();
27940 //this.editorsyncValue();
27942 onFirstFocus: function() {
27943 this.buttons.each(function(item){
27947 toggleSourceEdit : function(sourceEditMode){
27950 if(sourceEditMode){
27951 Roo.log("disabling buttons");
27952 this.buttons.each( function(item){
27953 if(item.cmd != 'pencil'){
27959 Roo.log("enabling buttons");
27960 if(this.editorcore.initialized){
27961 this.buttons.each( function(item){
27967 Roo.log("calling toggole on editor");
27968 // tell the editor that it's been pressed..
27969 this.editor.toggleSourceEdit(sourceEditMode);
27983 * @class Roo.bootstrap.Markdown
27984 * @extends Roo.bootstrap.TextArea
27985 * Bootstrap Showdown editable area
27986 * @cfg {string} content
27989 * Create a new Showdown
27992 Roo.bootstrap.Markdown = function(config){
27993 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27997 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
28001 initEvents : function()
28004 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28005 this.markdownEl = this.el.createChild({
28006 cls : 'roo-markdown-area'
28008 this.inputEl().addClass('d-none');
28009 if (this.getValue() == '') {
28010 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28013 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28015 this.markdownEl.on('click', this.toggleTextEdit, this);
28016 this.on('blur', this.toggleTextEdit, this);
28017 this.on('specialkey', this.resizeTextArea, this);
28020 toggleTextEdit : function()
28022 var sh = this.markdownEl.getHeight();
28023 this.inputEl().addClass('d-none');
28024 this.markdownEl.addClass('d-none');
28025 if (!this.editing) {
28027 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28028 this.inputEl().removeClass('d-none');
28029 this.inputEl().focus();
28030 this.editing = true;
28033 // show showdown...
28034 this.updateMarkdown();
28035 this.markdownEl.removeClass('d-none');
28036 this.editing = false;
28039 updateMarkdown : function()
28041 if (this.getValue() == '') {
28042 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28046 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28049 resizeTextArea: function () {
28052 Roo.log([sh, this.getValue().split("\n").length * 30]);
28053 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28055 setValue : function(val)
28057 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28058 if (!this.editing) {
28059 this.updateMarkdown();
28065 if (!this.editing) {
28066 this.toggleTextEdit();
28074 * Ext JS Library 1.1.1
28075 * Copyright(c) 2006-2007, Ext JS, LLC.
28077 * Originally Released Under LGPL - original licence link has changed is not relivant.
28080 * <script type="text/javascript">
28084 * @class Roo.bootstrap.PagingToolbar
28085 * @extends Roo.bootstrap.NavSimplebar
28086 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28088 * Create a new PagingToolbar
28089 * @param {Object} config The config object
28090 * @param {Roo.data.Store} store
28092 Roo.bootstrap.PagingToolbar = function(config)
28094 // old args format still supported... - xtype is prefered..
28095 // created from xtype...
28097 this.ds = config.dataSource;
28099 if (config.store && !this.ds) {
28100 this.store= Roo.factory(config.store, Roo.data);
28101 this.ds = this.store;
28102 this.ds.xmodule = this.xmodule || false;
28105 this.toolbarItems = [];
28106 if (config.items) {
28107 this.toolbarItems = config.items;
28110 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28115 this.bind(this.ds);
28118 if (Roo.bootstrap.version == 4) {
28119 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28121 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28126 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28128 * @cfg {Roo.data.Store} dataSource
28129 * The underlying data store providing the paged data
28132 * @cfg {String/HTMLElement/Element} container
28133 * container The id or element that will contain the toolbar
28136 * @cfg {Boolean} displayInfo
28137 * True to display the displayMsg (defaults to false)
28140 * @cfg {Number} pageSize
28141 * The number of records to display per page (defaults to 20)
28145 * @cfg {String} displayMsg
28146 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28148 displayMsg : 'Displaying {0} - {1} of {2}',
28150 * @cfg {String} emptyMsg
28151 * The message to display when no records are found (defaults to "No data to display")
28153 emptyMsg : 'No data to display',
28155 * Customizable piece of the default paging text (defaults to "Page")
28158 beforePageText : "Page",
28160 * Customizable piece of the default paging text (defaults to "of %0")
28163 afterPageText : "of {0}",
28165 * Customizable piece of the default paging text (defaults to "First Page")
28168 firstText : "First Page",
28170 * Customizable piece of the default paging text (defaults to "Previous Page")
28173 prevText : "Previous Page",
28175 * Customizable piece of the default paging text (defaults to "Next Page")
28178 nextText : "Next Page",
28180 * Customizable piece of the default paging text (defaults to "Last Page")
28183 lastText : "Last Page",
28185 * Customizable piece of the default paging text (defaults to "Refresh")
28188 refreshText : "Refresh",
28192 onRender : function(ct, position)
28194 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28195 this.navgroup.parentId = this.id;
28196 this.navgroup.onRender(this.el, null);
28197 // add the buttons to the navgroup
28199 if(this.displayInfo){
28200 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28201 this.displayEl = this.el.select('.x-paging-info', true).first();
28202 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28203 // this.displayEl = navel.el.select('span',true).first();
28209 Roo.each(_this.buttons, function(e){ // this might need to use render????
28210 Roo.factory(e).render(_this.el);
28214 Roo.each(_this.toolbarItems, function(e) {
28215 _this.navgroup.addItem(e);
28219 this.first = this.navgroup.addItem({
28220 tooltip: this.firstText,
28221 cls: "prev btn-outline-secondary",
28222 html : ' <i class="fa fa-step-backward"></i>',
28224 preventDefault: true,
28225 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28228 this.prev = this.navgroup.addItem({
28229 tooltip: this.prevText,
28230 cls: "prev btn-outline-secondary",
28231 html : ' <i class="fa fa-backward"></i>',
28233 preventDefault: true,
28234 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28236 //this.addSeparator();
28239 var field = this.navgroup.addItem( {
28241 cls : 'x-paging-position btn-outline-secondary',
28243 html : this.beforePageText +
28244 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28245 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28248 this.field = field.el.select('input', true).first();
28249 this.field.on("keydown", this.onPagingKeydown, this);
28250 this.field.on("focus", function(){this.dom.select();});
28253 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28254 //this.field.setHeight(18);
28255 //this.addSeparator();
28256 this.next = this.navgroup.addItem({
28257 tooltip: this.nextText,
28258 cls: "next btn-outline-secondary",
28259 html : ' <i class="fa fa-forward"></i>',
28261 preventDefault: true,
28262 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28264 this.last = this.navgroup.addItem({
28265 tooltip: this.lastText,
28266 html : ' <i class="fa fa-step-forward"></i>',
28267 cls: "next btn-outline-secondary",
28269 preventDefault: true,
28270 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28272 //this.addSeparator();
28273 this.loading = this.navgroup.addItem({
28274 tooltip: this.refreshText,
28275 cls: "btn-outline-secondary",
28276 html : ' <i class="fa fa-refresh"></i>',
28277 preventDefault: true,
28278 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28284 updateInfo : function(){
28285 if(this.displayEl){
28286 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28287 var msg = count == 0 ?
28291 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28293 this.displayEl.update(msg);
28298 onLoad : function(ds, r, o)
28300 this.cursor = o.params && o.params.start ? o.params.start : 0;
28302 var d = this.getPageData(),
28307 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28308 this.field.dom.value = ap;
28309 this.first.setDisabled(ap == 1);
28310 this.prev.setDisabled(ap == 1);
28311 this.next.setDisabled(ap == ps);
28312 this.last.setDisabled(ap == ps);
28313 this.loading.enable();
28318 getPageData : function(){
28319 var total = this.ds.getTotalCount();
28322 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28323 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28328 onLoadError : function(){
28329 this.loading.enable();
28333 onPagingKeydown : function(e){
28334 var k = e.getKey();
28335 var d = this.getPageData();
28337 var v = this.field.dom.value, pageNum;
28338 if(!v || isNaN(pageNum = parseInt(v, 10))){
28339 this.field.dom.value = d.activePage;
28342 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28343 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28346 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))
28348 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28349 this.field.dom.value = pageNum;
28350 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28353 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28355 var v = this.field.dom.value, pageNum;
28356 var increment = (e.shiftKey) ? 10 : 1;
28357 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28360 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28361 this.field.dom.value = d.activePage;
28364 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28366 this.field.dom.value = parseInt(v, 10) + increment;
28367 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28368 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28375 beforeLoad : function(){
28377 this.loading.disable();
28382 onClick : function(which){
28391 ds.load({params:{start: 0, limit: this.pageSize}});
28394 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28397 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28400 var total = ds.getTotalCount();
28401 var extra = total % this.pageSize;
28402 var lastStart = extra ? (total - extra) : total-this.pageSize;
28403 ds.load({params:{start: lastStart, limit: this.pageSize}});
28406 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28412 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28413 * @param {Roo.data.Store} store The data store to unbind
28415 unbind : function(ds){
28416 ds.un("beforeload", this.beforeLoad, this);
28417 ds.un("load", this.onLoad, this);
28418 ds.un("loadexception", this.onLoadError, this);
28419 ds.un("remove", this.updateInfo, this);
28420 ds.un("add", this.updateInfo, this);
28421 this.ds = undefined;
28425 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28426 * @param {Roo.data.Store} store The data store to bind
28428 bind : function(ds){
28429 ds.on("beforeload", this.beforeLoad, this);
28430 ds.on("load", this.onLoad, this);
28431 ds.on("loadexception", this.onLoadError, this);
28432 ds.on("remove", this.updateInfo, this);
28433 ds.on("add", this.updateInfo, this);
28444 * @class Roo.bootstrap.MessageBar
28445 * @extends Roo.bootstrap.Component
28446 * Bootstrap MessageBar class
28447 * @cfg {String} html contents of the MessageBar
28448 * @cfg {String} weight (info | success | warning | danger) default info
28449 * @cfg {String} beforeClass insert the bar before the given class
28450 * @cfg {Boolean} closable (true | false) default false
28451 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28454 * Create a new Element
28455 * @param {Object} config The config object
28458 Roo.bootstrap.MessageBar = function(config){
28459 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28462 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28468 beforeClass: 'bootstrap-sticky-wrap',
28470 getAutoCreate : function(){
28474 cls: 'alert alert-dismissable alert-' + this.weight,
28479 html: this.html || ''
28485 cfg.cls += ' alert-messages-fixed';
28499 onRender : function(ct, position)
28501 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28504 var cfg = Roo.apply({}, this.getAutoCreate());
28508 cfg.cls += ' ' + this.cls;
28511 cfg.style = this.style;
28513 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28515 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28518 this.el.select('>button.close').on('click', this.hide, this);
28524 if (!this.rendered) {
28530 this.fireEvent('show', this);
28536 if (!this.rendered) {
28542 this.fireEvent('hide', this);
28545 update : function()
28547 // var e = this.el.dom.firstChild;
28549 // if(this.closable){
28550 // e = e.nextSibling;
28553 // e.data = this.html || '';
28555 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28571 * @class Roo.bootstrap.Graph
28572 * @extends Roo.bootstrap.Component
28573 * Bootstrap Graph class
28577 @cfg {String} graphtype bar | vbar | pie
28578 @cfg {number} g_x coodinator | centre x (pie)
28579 @cfg {number} g_y coodinator | centre y (pie)
28580 @cfg {number} g_r radius (pie)
28581 @cfg {number} g_height height of the chart (respected by all elements in the set)
28582 @cfg {number} g_width width of the chart (respected by all elements in the set)
28583 @cfg {Object} title The title of the chart
28586 -opts (object) options for the chart
28588 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28589 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28591 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.
28592 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28594 o stretch (boolean)
28596 -opts (object) options for the pie
28599 o startAngle (number)
28600 o endAngle (number)
28604 * Create a new Input
28605 * @param {Object} config The config object
28608 Roo.bootstrap.Graph = function(config){
28609 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28615 * The img click event for the img.
28616 * @param {Roo.EventObject} e
28622 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28633 //g_colors: this.colors,
28640 getAutoCreate : function(){
28651 onRender : function(ct,position){
28654 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28656 if (typeof(Raphael) == 'undefined') {
28657 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28661 this.raphael = Raphael(this.el.dom);
28663 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28664 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28665 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28666 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28668 r.text(160, 10, "Single Series Chart").attr(txtattr);
28669 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28670 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28671 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28673 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28674 r.barchart(330, 10, 300, 220, data1);
28675 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28676 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28679 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28680 // r.barchart(30, 30, 560, 250, xdata, {
28681 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28682 // axis : "0 0 1 1",
28683 // axisxlabels : xdata
28684 // //yvalues : cols,
28687 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28689 // this.load(null,xdata,{
28690 // axis : "0 0 1 1",
28691 // axisxlabels : xdata
28696 load : function(graphtype,xdata,opts)
28698 this.raphael.clear();
28700 graphtype = this.graphtype;
28705 var r = this.raphael,
28706 fin = function () {
28707 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28709 fout = function () {
28710 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28712 pfin = function() {
28713 this.sector.stop();
28714 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28717 this.label[0].stop();
28718 this.label[0].attr({ r: 7.5 });
28719 this.label[1].attr({ "font-weight": 800 });
28722 pfout = function() {
28723 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28726 this.label[0].animate({ r: 5 }, 500, "bounce");
28727 this.label[1].attr({ "font-weight": 400 });
28733 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28736 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28739 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28740 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28742 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28749 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28754 setTitle: function(o)
28759 initEvents: function() {
28762 this.el.on('click', this.onClick, this);
28766 onClick : function(e)
28768 Roo.log('img onclick');
28769 this.fireEvent('click', this, e);
28781 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28784 * @class Roo.bootstrap.dash.NumberBox
28785 * @extends Roo.bootstrap.Component
28786 * Bootstrap NumberBox class
28787 * @cfg {String} headline Box headline
28788 * @cfg {String} content Box content
28789 * @cfg {String} icon Box icon
28790 * @cfg {String} footer Footer text
28791 * @cfg {String} fhref Footer href
28794 * Create a new NumberBox
28795 * @param {Object} config The config object
28799 Roo.bootstrap.dash.NumberBox = function(config){
28800 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28804 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28813 getAutoCreate : function(){
28817 cls : 'small-box ',
28825 cls : 'roo-headline',
28826 html : this.headline
28830 cls : 'roo-content',
28831 html : this.content
28845 cls : 'ion ' + this.icon
28854 cls : 'small-box-footer',
28855 href : this.fhref || '#',
28859 cfg.cn.push(footer);
28866 onRender : function(ct,position){
28867 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28874 setHeadline: function (value)
28876 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28879 setFooter: function (value, href)
28881 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28884 this.el.select('a.small-box-footer',true).first().attr('href', href);
28889 setContent: function (value)
28891 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28894 initEvents: function()
28908 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28911 * @class Roo.bootstrap.dash.TabBox
28912 * @extends Roo.bootstrap.Component
28913 * Bootstrap TabBox class
28914 * @cfg {String} title Title of the TabBox
28915 * @cfg {String} icon Icon of the TabBox
28916 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28917 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28920 * Create a new TabBox
28921 * @param {Object} config The config object
28925 Roo.bootstrap.dash.TabBox = function(config){
28926 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28931 * When a pane is added
28932 * @param {Roo.bootstrap.dash.TabPane} pane
28936 * @event activatepane
28937 * When a pane is activated
28938 * @param {Roo.bootstrap.dash.TabPane} pane
28940 "activatepane" : true
28948 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28953 tabScrollable : false,
28955 getChildContainer : function()
28957 return this.el.select('.tab-content', true).first();
28960 getAutoCreate : function(){
28964 cls: 'pull-left header',
28972 cls: 'fa ' + this.icon
28978 cls: 'nav nav-tabs pull-right',
28984 if(this.tabScrollable){
28991 cls: 'nav nav-tabs pull-right',
29002 cls: 'nav-tabs-custom',
29007 cls: 'tab-content no-padding',
29015 initEvents : function()
29017 //Roo.log('add add pane handler');
29018 this.on('addpane', this.onAddPane, this);
29021 * Updates the box title
29022 * @param {String} html to set the title to.
29024 setTitle : function(value)
29026 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29028 onAddPane : function(pane)
29030 this.panes.push(pane);
29031 //Roo.log('addpane');
29033 // tabs are rendere left to right..
29034 if(!this.showtabs){
29038 var ctr = this.el.select('.nav-tabs', true).first();
29041 var existing = ctr.select('.nav-tab',true);
29042 var qty = existing.getCount();;
29045 var tab = ctr.createChild({
29047 cls : 'nav-tab' + (qty ? '' : ' active'),
29055 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29058 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29060 pane.el.addClass('active');
29065 onTabClick : function(ev,un,ob,pane)
29067 //Roo.log('tab - prev default');
29068 ev.preventDefault();
29071 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29072 pane.tab.addClass('active');
29073 //Roo.log(pane.title);
29074 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29075 // technically we should have a deactivate event.. but maybe add later.
29076 // and it should not de-activate the selected tab...
29077 this.fireEvent('activatepane', pane);
29078 pane.el.addClass('active');
29079 pane.fireEvent('activate');
29084 getActivePane : function()
29087 Roo.each(this.panes, function(p) {
29088 if(p.el.hasClass('active')){
29109 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29111 * @class Roo.bootstrap.TabPane
29112 * @extends Roo.bootstrap.Component
29113 * Bootstrap TabPane class
29114 * @cfg {Boolean} active (false | true) Default false
29115 * @cfg {String} title title of panel
29119 * Create a new TabPane
29120 * @param {Object} config The config object
29123 Roo.bootstrap.dash.TabPane = function(config){
29124 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29130 * When a pane is activated
29131 * @param {Roo.bootstrap.dash.TabPane} pane
29138 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29143 // the tabBox that this is attached to.
29146 getAutoCreate : function()
29154 cfg.cls += ' active';
29159 initEvents : function()
29161 //Roo.log('trigger add pane handler');
29162 this.parent().fireEvent('addpane', this)
29166 * Updates the tab title
29167 * @param {String} html to set the title to.
29169 setTitle: function(str)
29175 this.tab.select('a', true).first().dom.innerHTML = str;
29192 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29195 * @class Roo.bootstrap.menu.Menu
29196 * @extends Roo.bootstrap.Component
29197 * Bootstrap Menu class - container for Menu
29198 * @cfg {String} html Text of the menu
29199 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29200 * @cfg {String} icon Font awesome icon
29201 * @cfg {String} pos Menu align to (top | bottom) default bottom
29205 * Create a new Menu
29206 * @param {Object} config The config object
29210 Roo.bootstrap.menu.Menu = function(config){
29211 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29215 * @event beforeshow
29216 * Fires before this menu is displayed
29217 * @param {Roo.bootstrap.menu.Menu} this
29221 * @event beforehide
29222 * Fires before this menu is hidden
29223 * @param {Roo.bootstrap.menu.Menu} this
29228 * Fires after this menu is displayed
29229 * @param {Roo.bootstrap.menu.Menu} this
29234 * Fires after this menu is hidden
29235 * @param {Roo.bootstrap.menu.Menu} this
29240 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29241 * @param {Roo.bootstrap.menu.Menu} this
29242 * @param {Roo.EventObject} e
29249 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29253 weight : 'default',
29258 getChildContainer : function() {
29259 if(this.isSubMenu){
29263 return this.el.select('ul.dropdown-menu', true).first();
29266 getAutoCreate : function()
29271 cls : 'roo-menu-text',
29279 cls : 'fa ' + this.icon
29290 cls : 'dropdown-button btn btn-' + this.weight,
29295 cls : 'dropdown-toggle btn btn-' + this.weight,
29305 cls : 'dropdown-menu'
29311 if(this.pos == 'top'){
29312 cfg.cls += ' dropup';
29315 if(this.isSubMenu){
29318 cls : 'dropdown-menu'
29325 onRender : function(ct, position)
29327 this.isSubMenu = ct.hasClass('dropdown-submenu');
29329 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29332 initEvents : function()
29334 if(this.isSubMenu){
29338 this.hidden = true;
29340 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29341 this.triggerEl.on('click', this.onTriggerPress, this);
29343 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29344 this.buttonEl.on('click', this.onClick, this);
29350 if(this.isSubMenu){
29354 return this.el.select('ul.dropdown-menu', true).first();
29357 onClick : function(e)
29359 this.fireEvent("click", this, e);
29362 onTriggerPress : function(e)
29364 if (this.isVisible()) {
29371 isVisible : function(){
29372 return !this.hidden;
29377 this.fireEvent("beforeshow", this);
29379 this.hidden = false;
29380 this.el.addClass('open');
29382 Roo.get(document).on("mouseup", this.onMouseUp, this);
29384 this.fireEvent("show", this);
29391 this.fireEvent("beforehide", this);
29393 this.hidden = true;
29394 this.el.removeClass('open');
29396 Roo.get(document).un("mouseup", this.onMouseUp);
29398 this.fireEvent("hide", this);
29401 onMouseUp : function()
29415 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29418 * @class Roo.bootstrap.menu.Item
29419 * @extends Roo.bootstrap.Component
29420 * Bootstrap MenuItem class
29421 * @cfg {Boolean} submenu (true | false) default false
29422 * @cfg {String} html text of the item
29423 * @cfg {String} href the link
29424 * @cfg {Boolean} disable (true | false) default false
29425 * @cfg {Boolean} preventDefault (true | false) default true
29426 * @cfg {String} icon Font awesome icon
29427 * @cfg {String} pos Submenu align to (left | right) default right
29431 * Create a new Item
29432 * @param {Object} config The config object
29436 Roo.bootstrap.menu.Item = function(config){
29437 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29441 * Fires when the mouse is hovering over this menu
29442 * @param {Roo.bootstrap.menu.Item} this
29443 * @param {Roo.EventObject} e
29448 * Fires when the mouse exits this menu
29449 * @param {Roo.bootstrap.menu.Item} this
29450 * @param {Roo.EventObject} e
29456 * The raw click event for the entire grid.
29457 * @param {Roo.EventObject} e
29463 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29468 preventDefault: true,
29473 getAutoCreate : function()
29478 cls : 'roo-menu-item-text',
29486 cls : 'fa ' + this.icon
29495 href : this.href || '#',
29502 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29506 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29508 if(this.pos == 'left'){
29509 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29516 initEvents : function()
29518 this.el.on('mouseover', this.onMouseOver, this);
29519 this.el.on('mouseout', this.onMouseOut, this);
29521 this.el.select('a', true).first().on('click', this.onClick, this);
29525 onClick : function(e)
29527 if(this.preventDefault){
29528 e.preventDefault();
29531 this.fireEvent("click", this, e);
29534 onMouseOver : function(e)
29536 if(this.submenu && this.pos == 'left'){
29537 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29540 this.fireEvent("mouseover", this, e);
29543 onMouseOut : function(e)
29545 this.fireEvent("mouseout", this, e);
29557 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29560 * @class Roo.bootstrap.menu.Separator
29561 * @extends Roo.bootstrap.Component
29562 * Bootstrap Separator class
29565 * Create a new Separator
29566 * @param {Object} config The config object
29570 Roo.bootstrap.menu.Separator = function(config){
29571 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29574 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29576 getAutoCreate : function(){
29579 cls: 'dropdown-divider divider'
29597 * @class Roo.bootstrap.Tooltip
29598 * Bootstrap Tooltip class
29599 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29600 * to determine which dom element triggers the tooltip.
29602 * It needs to add support for additional attributes like tooltip-position
29605 * Create a new Toolti
29606 * @param {Object} config The config object
29609 Roo.bootstrap.Tooltip = function(config){
29610 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29612 this.alignment = Roo.bootstrap.Tooltip.alignment;
29614 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29615 this.alignment = config.alignment;
29620 Roo.apply(Roo.bootstrap.Tooltip, {
29622 * @function init initialize tooltip monitoring.
29626 currentTip : false,
29627 currentRegion : false,
29633 Roo.get(document).on('mouseover', this.enter ,this);
29634 Roo.get(document).on('mouseout', this.leave, this);
29637 this.currentTip = new Roo.bootstrap.Tooltip();
29640 enter : function(ev)
29642 var dom = ev.getTarget();
29644 //Roo.log(['enter',dom]);
29645 var el = Roo.fly(dom);
29646 if (this.currentEl) {
29648 //Roo.log(this.currentEl);
29649 //Roo.log(this.currentEl.contains(dom));
29650 if (this.currentEl == el) {
29653 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29659 if (this.currentTip.el) {
29660 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29664 if(!el || el.dom == document){
29670 if (!el.attr('tooltip')) {
29671 pel = el.findParent("[tooltip]");
29673 bindEl = Roo.get(pel);
29679 // you can not look for children, as if el is the body.. then everythign is the child..
29680 if (!pel && !el.attr('tooltip')) { //
29681 if (!el.select("[tooltip]").elements.length) {
29684 // is the mouse over this child...?
29685 bindEl = el.select("[tooltip]").first();
29686 var xy = ev.getXY();
29687 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29688 //Roo.log("not in region.");
29691 //Roo.log("child element over..");
29694 this.currentEl = el;
29695 this.currentTip.bind(bindEl);
29696 this.currentRegion = Roo.lib.Region.getRegion(dom);
29697 this.currentTip.enter();
29700 leave : function(ev)
29702 var dom = ev.getTarget();
29703 //Roo.log(['leave',dom]);
29704 if (!this.currentEl) {
29709 if (dom != this.currentEl.dom) {
29712 var xy = ev.getXY();
29713 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29716 // only activate leave if mouse cursor is outside... bounding box..
29721 if (this.currentTip) {
29722 this.currentTip.leave();
29724 //Roo.log('clear currentEl');
29725 this.currentEl = false;
29730 'left' : ['r-l', [-2,0], 'right'],
29731 'right' : ['l-r', [2,0], 'left'],
29732 'bottom' : ['t-b', [0,2], 'top'],
29733 'top' : [ 'b-t', [0,-2], 'bottom']
29739 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29744 delay : null, // can be { show : 300 , hide: 500}
29748 hoverState : null, //???
29750 placement : 'bottom',
29754 getAutoCreate : function(){
29761 cls : 'tooltip-arrow arrow'
29764 cls : 'tooltip-inner'
29771 bind : function(el)
29776 initEvents : function()
29778 this.arrowEl = this.el.select('.arrow', true).first();
29779 this.innerEl = this.el.select('.tooltip-inner', true).first();
29782 enter : function () {
29784 if (this.timeout != null) {
29785 clearTimeout(this.timeout);
29788 this.hoverState = 'in';
29789 //Roo.log("enter - show");
29790 if (!this.delay || !this.delay.show) {
29795 this.timeout = setTimeout(function () {
29796 if (_t.hoverState == 'in') {
29799 }, this.delay.show);
29803 clearTimeout(this.timeout);
29805 this.hoverState = 'out';
29806 if (!this.delay || !this.delay.hide) {
29812 this.timeout = setTimeout(function () {
29813 //Roo.log("leave - timeout");
29815 if (_t.hoverState == 'out') {
29817 Roo.bootstrap.Tooltip.currentEl = false;
29822 show : function (msg)
29825 this.render(document.body);
29828 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29830 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29832 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29834 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29835 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29837 var placement = typeof this.placement == 'function' ?
29838 this.placement.call(this, this.el, on_el) :
29841 var autoToken = /\s?auto?\s?/i;
29842 var autoPlace = autoToken.test(placement);
29844 placement = placement.replace(autoToken, '') || 'top';
29848 //this.el.setXY([0,0]);
29850 //this.el.dom.style.display='block';
29852 //this.el.appendTo(on_el);
29854 var p = this.getPosition();
29855 var box = this.el.getBox();
29861 var align = this.alignment[placement];
29863 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29865 if(placement == 'top' || placement == 'bottom'){
29867 placement = 'right';
29870 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29871 placement = 'left';
29874 var scroll = Roo.select('body', true).first().getScroll();
29876 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29880 align = this.alignment[placement];
29882 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29886 var elems = document.getElementsByTagName('div');
29887 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29888 for (var i = 0; i < elems.length; i++) {
29889 var zindex = Number.parseInt(
29890 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29893 if (zindex > highest) {
29900 this.el.dom.style.zIndex = highest;
29902 this.el.alignTo(this.bindEl, align[0],align[1]);
29903 //var arrow = this.el.select('.arrow',true).first();
29904 //arrow.set(align[2],
29906 this.el.addClass(placement);
29907 this.el.addClass("bs-tooltip-"+ placement);
29909 this.el.addClass('in fade show');
29911 this.hoverState = null;
29913 if (this.el.hasClass('fade')) {
29928 //this.el.setXY([0,0]);
29929 this.el.removeClass(['show', 'in']);
29945 * @class Roo.bootstrap.LocationPicker
29946 * @extends Roo.bootstrap.Component
29947 * Bootstrap LocationPicker class
29948 * @cfg {Number} latitude Position when init default 0
29949 * @cfg {Number} longitude Position when init default 0
29950 * @cfg {Number} zoom default 15
29951 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29952 * @cfg {Boolean} mapTypeControl default false
29953 * @cfg {Boolean} disableDoubleClickZoom default false
29954 * @cfg {Boolean} scrollwheel default true
29955 * @cfg {Boolean} streetViewControl default false
29956 * @cfg {Number} radius default 0
29957 * @cfg {String} locationName
29958 * @cfg {Boolean} draggable default true
29959 * @cfg {Boolean} enableAutocomplete default false
29960 * @cfg {Boolean} enableReverseGeocode default true
29961 * @cfg {String} markerTitle
29964 * Create a new LocationPicker
29965 * @param {Object} config The config object
29969 Roo.bootstrap.LocationPicker = function(config){
29971 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29976 * Fires when the picker initialized.
29977 * @param {Roo.bootstrap.LocationPicker} this
29978 * @param {Google Location} location
29982 * @event positionchanged
29983 * Fires when the picker position changed.
29984 * @param {Roo.bootstrap.LocationPicker} this
29985 * @param {Google Location} location
29987 positionchanged : true,
29990 * Fires when the map resize.
29991 * @param {Roo.bootstrap.LocationPicker} this
29996 * Fires when the map show.
29997 * @param {Roo.bootstrap.LocationPicker} this
30002 * Fires when the map hide.
30003 * @param {Roo.bootstrap.LocationPicker} this
30008 * Fires when click the map.
30009 * @param {Roo.bootstrap.LocationPicker} this
30010 * @param {Map event} e
30014 * @event mapRightClick
30015 * Fires when right click the map.
30016 * @param {Roo.bootstrap.LocationPicker} this
30017 * @param {Map event} e
30019 mapRightClick : true,
30021 * @event markerClick
30022 * Fires when click the marker.
30023 * @param {Roo.bootstrap.LocationPicker} this
30024 * @param {Map event} e
30026 markerClick : true,
30028 * @event markerRightClick
30029 * Fires when right click the marker.
30030 * @param {Roo.bootstrap.LocationPicker} this
30031 * @param {Map event} e
30033 markerRightClick : true,
30035 * @event OverlayViewDraw
30036 * Fires when OverlayView Draw
30037 * @param {Roo.bootstrap.LocationPicker} this
30039 OverlayViewDraw : true,
30041 * @event OverlayViewOnAdd
30042 * Fires when OverlayView Draw
30043 * @param {Roo.bootstrap.LocationPicker} this
30045 OverlayViewOnAdd : true,
30047 * @event OverlayViewOnRemove
30048 * Fires when OverlayView Draw
30049 * @param {Roo.bootstrap.LocationPicker} this
30051 OverlayViewOnRemove : true,
30053 * @event OverlayViewShow
30054 * Fires when OverlayView Draw
30055 * @param {Roo.bootstrap.LocationPicker} this
30056 * @param {Pixel} cpx
30058 OverlayViewShow : true,
30060 * @event OverlayViewHide
30061 * Fires when OverlayView Draw
30062 * @param {Roo.bootstrap.LocationPicker} this
30064 OverlayViewHide : true,
30066 * @event loadexception
30067 * Fires when load google lib failed.
30068 * @param {Roo.bootstrap.LocationPicker} this
30070 loadexception : true
30075 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30077 gMapContext: false,
30083 mapTypeControl: false,
30084 disableDoubleClickZoom: false,
30086 streetViewControl: false,
30090 enableAutocomplete: false,
30091 enableReverseGeocode: true,
30094 getAutoCreate: function()
30099 cls: 'roo-location-picker'
30105 initEvents: function(ct, position)
30107 if(!this.el.getWidth() || this.isApplied()){
30111 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30116 initial: function()
30118 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30119 this.fireEvent('loadexception', this);
30123 if(!this.mapTypeId){
30124 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30127 this.gMapContext = this.GMapContext();
30129 this.initOverlayView();
30131 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30135 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30136 _this.setPosition(_this.gMapContext.marker.position);
30139 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30140 _this.fireEvent('mapClick', this, event);
30144 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30145 _this.fireEvent('mapRightClick', this, event);
30149 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30150 _this.fireEvent('markerClick', this, event);
30154 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30155 _this.fireEvent('markerRightClick', this, event);
30159 this.setPosition(this.gMapContext.location);
30161 this.fireEvent('initial', this, this.gMapContext.location);
30164 initOverlayView: function()
30168 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30172 _this.fireEvent('OverlayViewDraw', _this);
30177 _this.fireEvent('OverlayViewOnAdd', _this);
30180 onRemove: function()
30182 _this.fireEvent('OverlayViewOnRemove', _this);
30185 show: function(cpx)
30187 _this.fireEvent('OverlayViewShow', _this, cpx);
30192 _this.fireEvent('OverlayViewHide', _this);
30198 fromLatLngToContainerPixel: function(event)
30200 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30203 isApplied: function()
30205 return this.getGmapContext() == false ? false : true;
30208 getGmapContext: function()
30210 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30213 GMapContext: function()
30215 var position = new google.maps.LatLng(this.latitude, this.longitude);
30217 var _map = new google.maps.Map(this.el.dom, {
30220 mapTypeId: this.mapTypeId,
30221 mapTypeControl: this.mapTypeControl,
30222 disableDoubleClickZoom: this.disableDoubleClickZoom,
30223 scrollwheel: this.scrollwheel,
30224 streetViewControl: this.streetViewControl,
30225 locationName: this.locationName,
30226 draggable: this.draggable,
30227 enableAutocomplete: this.enableAutocomplete,
30228 enableReverseGeocode: this.enableReverseGeocode
30231 var _marker = new google.maps.Marker({
30232 position: position,
30234 title: this.markerTitle,
30235 draggable: this.draggable
30242 location: position,
30243 radius: this.radius,
30244 locationName: this.locationName,
30245 addressComponents: {
30246 formatted_address: null,
30247 addressLine1: null,
30248 addressLine2: null,
30250 streetNumber: null,
30254 stateOrProvince: null
30257 domContainer: this.el.dom,
30258 geodecoder: new google.maps.Geocoder()
30262 drawCircle: function(center, radius, options)
30264 if (this.gMapContext.circle != null) {
30265 this.gMapContext.circle.setMap(null);
30269 options = Roo.apply({}, options, {
30270 strokeColor: "#0000FF",
30271 strokeOpacity: .35,
30273 fillColor: "#0000FF",
30277 options.map = this.gMapContext.map;
30278 options.radius = radius;
30279 options.center = center;
30280 this.gMapContext.circle = new google.maps.Circle(options);
30281 return this.gMapContext.circle;
30287 setPosition: function(location)
30289 this.gMapContext.location = location;
30290 this.gMapContext.marker.setPosition(location);
30291 this.gMapContext.map.panTo(location);
30292 this.drawCircle(location, this.gMapContext.radius, {});
30296 if (this.gMapContext.settings.enableReverseGeocode) {
30297 this.gMapContext.geodecoder.geocode({
30298 latLng: this.gMapContext.location
30299 }, function(results, status) {
30301 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30302 _this.gMapContext.locationName = results[0].formatted_address;
30303 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30305 _this.fireEvent('positionchanged', this, location);
30312 this.fireEvent('positionchanged', this, location);
30317 google.maps.event.trigger(this.gMapContext.map, "resize");
30319 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30321 this.fireEvent('resize', this);
30324 setPositionByLatLng: function(latitude, longitude)
30326 this.setPosition(new google.maps.LatLng(latitude, longitude));
30329 getCurrentPosition: function()
30332 latitude: this.gMapContext.location.lat(),
30333 longitude: this.gMapContext.location.lng()
30337 getAddressName: function()
30339 return this.gMapContext.locationName;
30342 getAddressComponents: function()
30344 return this.gMapContext.addressComponents;
30347 address_component_from_google_geocode: function(address_components)
30351 for (var i = 0; i < address_components.length; i++) {
30352 var component = address_components[i];
30353 if (component.types.indexOf("postal_code") >= 0) {
30354 result.postalCode = component.short_name;
30355 } else if (component.types.indexOf("street_number") >= 0) {
30356 result.streetNumber = component.short_name;
30357 } else if (component.types.indexOf("route") >= 0) {
30358 result.streetName = component.short_name;
30359 } else if (component.types.indexOf("neighborhood") >= 0) {
30360 result.city = component.short_name;
30361 } else if (component.types.indexOf("locality") >= 0) {
30362 result.city = component.short_name;
30363 } else if (component.types.indexOf("sublocality") >= 0) {
30364 result.district = component.short_name;
30365 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30366 result.stateOrProvince = component.short_name;
30367 } else if (component.types.indexOf("country") >= 0) {
30368 result.country = component.short_name;
30372 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30373 result.addressLine2 = "";
30377 setZoomLevel: function(zoom)
30379 this.gMapContext.map.setZoom(zoom);
30392 this.fireEvent('show', this);
30403 this.fireEvent('hide', this);
30408 Roo.apply(Roo.bootstrap.LocationPicker, {
30410 OverlayView : function(map, options)
30412 options = options || {};
30419 * @class Roo.bootstrap.Alert
30420 * @extends Roo.bootstrap.Component
30421 * Bootstrap Alert class - shows an alert area box
30423 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30424 Enter a valid email address
30427 * @cfg {String} title The title of alert
30428 * @cfg {String} html The content of alert
30429 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30430 * @cfg {String} fa font-awesomeicon
30431 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30432 * @cfg {Boolean} close true to show a x closer
30436 * Create a new alert
30437 * @param {Object} config The config object
30441 Roo.bootstrap.Alert = function(config){
30442 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30446 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30452 faicon: false, // BC
30456 getAutoCreate : function()
30468 style : this.close ? '' : 'display:none'
30472 cls : 'roo-alert-icon'
30477 cls : 'roo-alert-title',
30482 cls : 'roo-alert-text',
30489 cfg.cn[0].cls += ' fa ' + this.faicon;
30492 cfg.cn[0].cls += ' fa ' + this.fa;
30496 cfg.cls += ' alert-' + this.weight;
30502 initEvents: function()
30504 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30505 this.titleEl = this.el.select('.roo-alert-title',true).first();
30506 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30507 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30508 if (this.seconds > 0) {
30509 this.hide.defer(this.seconds, this);
30513 * Set the Title Message HTML
30514 * @param {String} html
30516 setTitle : function(str)
30518 this.titleEl.dom.innerHTML = str;
30522 * Set the Body Message HTML
30523 * @param {String} html
30525 setHtml : function(str)
30527 this.htmlEl.dom.innerHTML = str;
30530 * Set the Weight of the alert
30531 * @param {String} (success|info|warning|danger) weight
30534 setWeight : function(weight)
30537 this.el.removeClass('alert-' + this.weight);
30540 this.weight = weight;
30542 this.el.addClass('alert-' + this.weight);
30545 * Set the Icon of the alert
30546 * @param {String} see fontawsome names (name without the 'fa-' bit)
30548 setIcon : function(icon)
30551 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30554 this.faicon = icon;
30556 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30581 * @class Roo.bootstrap.UploadCropbox
30582 * @extends Roo.bootstrap.Component
30583 * Bootstrap UploadCropbox class
30584 * @cfg {String} emptyText show when image has been loaded
30585 * @cfg {String} rotateNotify show when image too small to rotate
30586 * @cfg {Number} errorTimeout default 3000
30587 * @cfg {Number} minWidth default 300
30588 * @cfg {Number} minHeight default 300
30589 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30590 * @cfg {Boolean} isDocument (true|false) default false
30591 * @cfg {String} url action url
30592 * @cfg {String} paramName default 'imageUpload'
30593 * @cfg {String} method default POST
30594 * @cfg {Boolean} loadMask (true|false) default true
30595 * @cfg {Boolean} loadingText default 'Loading...'
30598 * Create a new UploadCropbox
30599 * @param {Object} config The config object
30602 Roo.bootstrap.UploadCropbox = function(config){
30603 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30607 * @event beforeselectfile
30608 * Fire before select file
30609 * @param {Roo.bootstrap.UploadCropbox} this
30611 "beforeselectfile" : true,
30614 * Fire after initEvent
30615 * @param {Roo.bootstrap.UploadCropbox} this
30620 * Fire after initEvent
30621 * @param {Roo.bootstrap.UploadCropbox} this
30622 * @param {String} data
30627 * Fire when preparing the file data
30628 * @param {Roo.bootstrap.UploadCropbox} this
30629 * @param {Object} file
30634 * Fire when get exception
30635 * @param {Roo.bootstrap.UploadCropbox} this
30636 * @param {XMLHttpRequest} xhr
30638 "exception" : true,
30640 * @event beforeloadcanvas
30641 * Fire before load the canvas
30642 * @param {Roo.bootstrap.UploadCropbox} this
30643 * @param {String} src
30645 "beforeloadcanvas" : true,
30648 * Fire when trash image
30649 * @param {Roo.bootstrap.UploadCropbox} this
30654 * Fire when download the image
30655 * @param {Roo.bootstrap.UploadCropbox} this
30659 * @event footerbuttonclick
30660 * Fire when footerbuttonclick
30661 * @param {Roo.bootstrap.UploadCropbox} this
30662 * @param {String} type
30664 "footerbuttonclick" : true,
30668 * @param {Roo.bootstrap.UploadCropbox} this
30673 * Fire when rotate the image
30674 * @param {Roo.bootstrap.UploadCropbox} this
30675 * @param {String} pos
30680 * Fire when inspect the file
30681 * @param {Roo.bootstrap.UploadCropbox} this
30682 * @param {Object} file
30687 * Fire when xhr upload the file
30688 * @param {Roo.bootstrap.UploadCropbox} this
30689 * @param {Object} data
30694 * Fire when arrange the file data
30695 * @param {Roo.bootstrap.UploadCropbox} this
30696 * @param {Object} formData
30701 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30704 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30706 emptyText : 'Click to upload image',
30707 rotateNotify : 'Image is too small to rotate',
30708 errorTimeout : 3000,
30722 cropType : 'image/jpeg',
30724 canvasLoaded : false,
30725 isDocument : false,
30727 paramName : 'imageUpload',
30729 loadingText : 'Loading...',
30732 getAutoCreate : function()
30736 cls : 'roo-upload-cropbox',
30740 cls : 'roo-upload-cropbox-selector',
30745 cls : 'roo-upload-cropbox-body',
30746 style : 'cursor:pointer',
30750 cls : 'roo-upload-cropbox-preview'
30754 cls : 'roo-upload-cropbox-thumb'
30758 cls : 'roo-upload-cropbox-empty-notify',
30759 html : this.emptyText
30763 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30764 html : this.rotateNotify
30770 cls : 'roo-upload-cropbox-footer',
30773 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30783 onRender : function(ct, position)
30785 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30787 if (this.buttons.length) {
30789 Roo.each(this.buttons, function(bb) {
30791 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30793 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30799 this.maskEl = this.el;
30803 initEvents : function()
30805 this.urlAPI = (window.createObjectURL && window) ||
30806 (window.URL && URL.revokeObjectURL && URL) ||
30807 (window.webkitURL && webkitURL);
30809 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30810 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30812 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30813 this.selectorEl.hide();
30815 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30816 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30818 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30819 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30820 this.thumbEl.hide();
30822 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30823 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30825 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30826 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30827 this.errorEl.hide();
30829 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30830 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30831 this.footerEl.hide();
30833 this.setThumbBoxSize();
30839 this.fireEvent('initial', this);
30846 window.addEventListener("resize", function() { _this.resize(); } );
30848 this.bodyEl.on('click', this.beforeSelectFile, this);
30851 this.bodyEl.on('touchstart', this.onTouchStart, this);
30852 this.bodyEl.on('touchmove', this.onTouchMove, this);
30853 this.bodyEl.on('touchend', this.onTouchEnd, this);
30857 this.bodyEl.on('mousedown', this.onMouseDown, this);
30858 this.bodyEl.on('mousemove', this.onMouseMove, this);
30859 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30860 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30861 Roo.get(document).on('mouseup', this.onMouseUp, this);
30864 this.selectorEl.on('change', this.onFileSelected, this);
30870 this.baseScale = 1;
30872 this.baseRotate = 1;
30873 this.dragable = false;
30874 this.pinching = false;
30877 this.cropData = false;
30878 this.notifyEl.dom.innerHTML = this.emptyText;
30880 this.selectorEl.dom.value = '';
30884 resize : function()
30886 if(this.fireEvent('resize', this) != false){
30887 this.setThumbBoxPosition();
30888 this.setCanvasPosition();
30892 onFooterButtonClick : function(e, el, o, type)
30895 case 'rotate-left' :
30896 this.onRotateLeft(e);
30898 case 'rotate-right' :
30899 this.onRotateRight(e);
30902 this.beforeSelectFile(e);
30917 this.fireEvent('footerbuttonclick', this, type);
30920 beforeSelectFile : function(e)
30922 e.preventDefault();
30924 if(this.fireEvent('beforeselectfile', this) != false){
30925 this.selectorEl.dom.click();
30929 onFileSelected : function(e)
30931 e.preventDefault();
30933 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30937 var file = this.selectorEl.dom.files[0];
30939 if(this.fireEvent('inspect', this, file) != false){
30940 this.prepare(file);
30945 trash : function(e)
30947 this.fireEvent('trash', this);
30950 download : function(e)
30952 this.fireEvent('download', this);
30955 loadCanvas : function(src)
30957 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30961 this.imageEl = document.createElement('img');
30965 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30967 this.imageEl.src = src;
30971 onLoadCanvas : function()
30973 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30974 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30976 this.bodyEl.un('click', this.beforeSelectFile, this);
30978 this.notifyEl.hide();
30979 this.thumbEl.show();
30980 this.footerEl.show();
30982 this.baseRotateLevel();
30984 if(this.isDocument){
30985 this.setThumbBoxSize();
30988 this.setThumbBoxPosition();
30990 this.baseScaleLevel();
30996 this.canvasLoaded = true;
30999 this.maskEl.unmask();
31004 setCanvasPosition : function()
31006 if(!this.canvasEl){
31010 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31011 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31013 this.previewEl.setLeft(pw);
31014 this.previewEl.setTop(ph);
31018 onMouseDown : function(e)
31022 this.dragable = true;
31023 this.pinching = false;
31025 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31026 this.dragable = false;
31030 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31031 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31035 onMouseMove : function(e)
31039 if(!this.canvasLoaded){
31043 if (!this.dragable){
31047 var minX = Math.ceil(this.thumbEl.getLeft(true));
31048 var minY = Math.ceil(this.thumbEl.getTop(true));
31050 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31051 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31053 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31054 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31056 x = x - this.mouseX;
31057 y = y - this.mouseY;
31059 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31060 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31062 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31063 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31065 this.previewEl.setLeft(bgX);
31066 this.previewEl.setTop(bgY);
31068 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31069 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31072 onMouseUp : function(e)
31076 this.dragable = false;
31079 onMouseWheel : function(e)
31083 this.startScale = this.scale;
31085 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31087 if(!this.zoomable()){
31088 this.scale = this.startScale;
31097 zoomable : function()
31099 var minScale = this.thumbEl.getWidth() / this.minWidth;
31101 if(this.minWidth < this.minHeight){
31102 minScale = this.thumbEl.getHeight() / this.minHeight;
31105 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31106 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31110 (this.rotate == 0 || this.rotate == 180) &&
31112 width > this.imageEl.OriginWidth ||
31113 height > this.imageEl.OriginHeight ||
31114 (width < this.minWidth && height < this.minHeight)
31122 (this.rotate == 90 || this.rotate == 270) &&
31124 width > this.imageEl.OriginWidth ||
31125 height > this.imageEl.OriginHeight ||
31126 (width < this.minHeight && height < this.minWidth)
31133 !this.isDocument &&
31134 (this.rotate == 0 || this.rotate == 180) &&
31136 width < this.minWidth ||
31137 width > this.imageEl.OriginWidth ||
31138 height < this.minHeight ||
31139 height > this.imageEl.OriginHeight
31146 !this.isDocument &&
31147 (this.rotate == 90 || this.rotate == 270) &&
31149 width < this.minHeight ||
31150 width > this.imageEl.OriginWidth ||
31151 height < this.minWidth ||
31152 height > this.imageEl.OriginHeight
31162 onRotateLeft : function(e)
31164 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31166 var minScale = this.thumbEl.getWidth() / this.minWidth;
31168 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31169 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31171 this.startScale = this.scale;
31173 while (this.getScaleLevel() < minScale){
31175 this.scale = this.scale + 1;
31177 if(!this.zoomable()){
31182 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31183 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31188 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31195 this.scale = this.startScale;
31197 this.onRotateFail();
31202 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31204 if(this.isDocument){
31205 this.setThumbBoxSize();
31206 this.setThumbBoxPosition();
31207 this.setCanvasPosition();
31212 this.fireEvent('rotate', this, 'left');
31216 onRotateRight : function(e)
31218 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31220 var minScale = this.thumbEl.getWidth() / this.minWidth;
31222 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31223 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31225 this.startScale = this.scale;
31227 while (this.getScaleLevel() < minScale){
31229 this.scale = this.scale + 1;
31231 if(!this.zoomable()){
31236 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31237 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31242 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31249 this.scale = this.startScale;
31251 this.onRotateFail();
31256 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31258 if(this.isDocument){
31259 this.setThumbBoxSize();
31260 this.setThumbBoxPosition();
31261 this.setCanvasPosition();
31266 this.fireEvent('rotate', this, 'right');
31269 onRotateFail : function()
31271 this.errorEl.show(true);
31275 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31280 this.previewEl.dom.innerHTML = '';
31282 var canvasEl = document.createElement("canvas");
31284 var contextEl = canvasEl.getContext("2d");
31286 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31287 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31288 var center = this.imageEl.OriginWidth / 2;
31290 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31291 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31292 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31293 center = this.imageEl.OriginHeight / 2;
31296 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31298 contextEl.translate(center, center);
31299 contextEl.rotate(this.rotate * Math.PI / 180);
31301 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31303 this.canvasEl = document.createElement("canvas");
31305 this.contextEl = this.canvasEl.getContext("2d");
31307 switch (this.rotate) {
31310 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31311 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31313 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31318 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31319 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31321 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31322 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31326 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31331 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31332 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31334 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31335 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31339 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);
31344 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31345 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31347 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31348 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31352 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);
31359 this.previewEl.appendChild(this.canvasEl);
31361 this.setCanvasPosition();
31366 if(!this.canvasLoaded){
31370 var imageCanvas = document.createElement("canvas");
31372 var imageContext = imageCanvas.getContext("2d");
31374 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31375 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31377 var center = imageCanvas.width / 2;
31379 imageContext.translate(center, center);
31381 imageContext.rotate(this.rotate * Math.PI / 180);
31383 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31385 var canvas = document.createElement("canvas");
31387 var context = canvas.getContext("2d");
31389 canvas.width = this.minWidth;
31390 canvas.height = this.minHeight;
31392 switch (this.rotate) {
31395 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31396 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31398 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31399 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31401 var targetWidth = this.minWidth - 2 * x;
31402 var targetHeight = this.minHeight - 2 * y;
31406 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31407 scale = targetWidth / width;
31410 if(x > 0 && y == 0){
31411 scale = targetHeight / height;
31414 if(x > 0 && y > 0){
31415 scale = targetWidth / width;
31417 if(width < height){
31418 scale = targetHeight / height;
31422 context.scale(scale, scale);
31424 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31425 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31427 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31428 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31430 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31435 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31436 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31438 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31439 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31441 var targetWidth = this.minWidth - 2 * x;
31442 var targetHeight = this.minHeight - 2 * y;
31446 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31447 scale = targetWidth / width;
31450 if(x > 0 && y == 0){
31451 scale = targetHeight / height;
31454 if(x > 0 && y > 0){
31455 scale = targetWidth / width;
31457 if(width < height){
31458 scale = targetHeight / height;
31462 context.scale(scale, scale);
31464 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31465 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31467 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31468 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31470 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31472 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31477 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31478 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31480 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31481 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31483 var targetWidth = this.minWidth - 2 * x;
31484 var targetHeight = this.minHeight - 2 * y;
31488 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31489 scale = targetWidth / width;
31492 if(x > 0 && y == 0){
31493 scale = targetHeight / height;
31496 if(x > 0 && y > 0){
31497 scale = targetWidth / width;
31499 if(width < height){
31500 scale = targetHeight / height;
31504 context.scale(scale, scale);
31506 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31507 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31509 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31510 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31512 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31513 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31515 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31520 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31521 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31523 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31524 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31526 var targetWidth = this.minWidth - 2 * x;
31527 var targetHeight = this.minHeight - 2 * y;
31531 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31532 scale = targetWidth / width;
31535 if(x > 0 && y == 0){
31536 scale = targetHeight / height;
31539 if(x > 0 && y > 0){
31540 scale = targetWidth / width;
31542 if(width < height){
31543 scale = targetHeight / height;
31547 context.scale(scale, scale);
31549 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31550 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31552 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31553 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31555 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31557 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31564 this.cropData = canvas.toDataURL(this.cropType);
31566 if(this.fireEvent('crop', this, this.cropData) !== false){
31567 this.process(this.file, this.cropData);
31574 setThumbBoxSize : function()
31578 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31579 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31580 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31582 this.minWidth = width;
31583 this.minHeight = height;
31585 if(this.rotate == 90 || this.rotate == 270){
31586 this.minWidth = height;
31587 this.minHeight = width;
31592 width = Math.ceil(this.minWidth * height / this.minHeight);
31594 if(this.minWidth > this.minHeight){
31596 height = Math.ceil(this.minHeight * width / this.minWidth);
31599 this.thumbEl.setStyle({
31600 width : width + 'px',
31601 height : height + 'px'
31608 setThumbBoxPosition : function()
31610 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31611 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31613 this.thumbEl.setLeft(x);
31614 this.thumbEl.setTop(y);
31618 baseRotateLevel : function()
31620 this.baseRotate = 1;
31623 typeof(this.exif) != 'undefined' &&
31624 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31625 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31627 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31630 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31634 baseScaleLevel : function()
31638 if(this.isDocument){
31640 if(this.baseRotate == 6 || this.baseRotate == 8){
31642 height = this.thumbEl.getHeight();
31643 this.baseScale = height / this.imageEl.OriginWidth;
31645 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31646 width = this.thumbEl.getWidth();
31647 this.baseScale = width / this.imageEl.OriginHeight;
31653 height = this.thumbEl.getHeight();
31654 this.baseScale = height / this.imageEl.OriginHeight;
31656 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31657 width = this.thumbEl.getWidth();
31658 this.baseScale = width / this.imageEl.OriginWidth;
31664 if(this.baseRotate == 6 || this.baseRotate == 8){
31666 width = this.thumbEl.getHeight();
31667 this.baseScale = width / this.imageEl.OriginHeight;
31669 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31670 height = this.thumbEl.getWidth();
31671 this.baseScale = height / this.imageEl.OriginHeight;
31674 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31675 height = this.thumbEl.getWidth();
31676 this.baseScale = height / this.imageEl.OriginHeight;
31678 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31679 width = this.thumbEl.getHeight();
31680 this.baseScale = width / this.imageEl.OriginWidth;
31687 width = this.thumbEl.getWidth();
31688 this.baseScale = width / this.imageEl.OriginWidth;
31690 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31691 height = this.thumbEl.getHeight();
31692 this.baseScale = height / this.imageEl.OriginHeight;
31695 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31697 height = this.thumbEl.getHeight();
31698 this.baseScale = height / this.imageEl.OriginHeight;
31700 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31701 width = this.thumbEl.getWidth();
31702 this.baseScale = width / this.imageEl.OriginWidth;
31710 getScaleLevel : function()
31712 return this.baseScale * Math.pow(1.1, this.scale);
31715 onTouchStart : function(e)
31717 if(!this.canvasLoaded){
31718 this.beforeSelectFile(e);
31722 var touches = e.browserEvent.touches;
31728 if(touches.length == 1){
31729 this.onMouseDown(e);
31733 if(touches.length != 2){
31739 for(var i = 0, finger; finger = touches[i]; i++){
31740 coords.push(finger.pageX, finger.pageY);
31743 var x = Math.pow(coords[0] - coords[2], 2);
31744 var y = Math.pow(coords[1] - coords[3], 2);
31746 this.startDistance = Math.sqrt(x + y);
31748 this.startScale = this.scale;
31750 this.pinching = true;
31751 this.dragable = false;
31755 onTouchMove : function(e)
31757 if(!this.pinching && !this.dragable){
31761 var touches = e.browserEvent.touches;
31768 this.onMouseMove(e);
31774 for(var i = 0, finger; finger = touches[i]; i++){
31775 coords.push(finger.pageX, finger.pageY);
31778 var x = Math.pow(coords[0] - coords[2], 2);
31779 var y = Math.pow(coords[1] - coords[3], 2);
31781 this.endDistance = Math.sqrt(x + y);
31783 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31785 if(!this.zoomable()){
31786 this.scale = this.startScale;
31794 onTouchEnd : function(e)
31796 this.pinching = false;
31797 this.dragable = false;
31801 process : function(file, crop)
31804 this.maskEl.mask(this.loadingText);
31807 this.xhr = new XMLHttpRequest();
31809 file.xhr = this.xhr;
31811 this.xhr.open(this.method, this.url, true);
31814 "Accept": "application/json",
31815 "Cache-Control": "no-cache",
31816 "X-Requested-With": "XMLHttpRequest"
31819 for (var headerName in headers) {
31820 var headerValue = headers[headerName];
31822 this.xhr.setRequestHeader(headerName, headerValue);
31828 this.xhr.onload = function()
31830 _this.xhrOnLoad(_this.xhr);
31833 this.xhr.onerror = function()
31835 _this.xhrOnError(_this.xhr);
31838 var formData = new FormData();
31840 formData.append('returnHTML', 'NO');
31843 formData.append('crop', crop);
31846 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31847 formData.append(this.paramName, file, file.name);
31850 if(typeof(file.filename) != 'undefined'){
31851 formData.append('filename', file.filename);
31854 if(typeof(file.mimetype) != 'undefined'){
31855 formData.append('mimetype', file.mimetype);
31858 if(this.fireEvent('arrange', this, formData) != false){
31859 this.xhr.send(formData);
31863 xhrOnLoad : function(xhr)
31866 this.maskEl.unmask();
31869 if (xhr.readyState !== 4) {
31870 this.fireEvent('exception', this, xhr);
31874 var response = Roo.decode(xhr.responseText);
31876 if(!response.success){
31877 this.fireEvent('exception', this, xhr);
31881 var response = Roo.decode(xhr.responseText);
31883 this.fireEvent('upload', this, response);
31887 xhrOnError : function()
31890 this.maskEl.unmask();
31893 Roo.log('xhr on error');
31895 var response = Roo.decode(xhr.responseText);
31901 prepare : function(file)
31904 this.maskEl.mask(this.loadingText);
31910 if(typeof(file) === 'string'){
31911 this.loadCanvas(file);
31915 if(!file || !this.urlAPI){
31920 this.cropType = file.type;
31924 if(this.fireEvent('prepare', this, this.file) != false){
31926 var reader = new FileReader();
31928 reader.onload = function (e) {
31929 if (e.target.error) {
31930 Roo.log(e.target.error);
31934 var buffer = e.target.result,
31935 dataView = new DataView(buffer),
31937 maxOffset = dataView.byteLength - 4,
31941 if (dataView.getUint16(0) === 0xffd8) {
31942 while (offset < maxOffset) {
31943 markerBytes = dataView.getUint16(offset);
31945 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31946 markerLength = dataView.getUint16(offset + 2) + 2;
31947 if (offset + markerLength > dataView.byteLength) {
31948 Roo.log('Invalid meta data: Invalid segment size.');
31952 if(markerBytes == 0xffe1){
31953 _this.parseExifData(
31960 offset += markerLength;
31970 var url = _this.urlAPI.createObjectURL(_this.file);
31972 _this.loadCanvas(url);
31977 reader.readAsArrayBuffer(this.file);
31983 parseExifData : function(dataView, offset, length)
31985 var tiffOffset = offset + 10,
31989 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31990 // No Exif data, might be XMP data instead
31994 // Check for the ASCII code for "Exif" (0x45786966):
31995 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31996 // No Exif data, might be XMP data instead
31999 if (tiffOffset + 8 > dataView.byteLength) {
32000 Roo.log('Invalid Exif data: Invalid segment size.');
32003 // Check for the two null bytes:
32004 if (dataView.getUint16(offset + 8) !== 0x0000) {
32005 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32008 // Check the byte alignment:
32009 switch (dataView.getUint16(tiffOffset)) {
32011 littleEndian = true;
32014 littleEndian = false;
32017 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32020 // Check for the TIFF tag marker (0x002A):
32021 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32022 Roo.log('Invalid Exif data: Missing TIFF marker.');
32025 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32026 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32028 this.parseExifTags(
32031 tiffOffset + dirOffset,
32036 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32041 if (dirOffset + 6 > dataView.byteLength) {
32042 Roo.log('Invalid Exif data: Invalid directory offset.');
32045 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32046 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32047 if (dirEndOffset + 4 > dataView.byteLength) {
32048 Roo.log('Invalid Exif data: Invalid directory size.');
32051 for (i = 0; i < tagsNumber; i += 1) {
32055 dirOffset + 2 + 12 * i, // tag offset
32059 // Return the offset to the next directory:
32060 return dataView.getUint32(dirEndOffset, littleEndian);
32063 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32065 var tag = dataView.getUint16(offset, littleEndian);
32067 this.exif[tag] = this.getExifValue(
32071 dataView.getUint16(offset + 2, littleEndian), // tag type
32072 dataView.getUint32(offset + 4, littleEndian), // tag length
32077 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32079 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32088 Roo.log('Invalid Exif data: Invalid tag type.');
32092 tagSize = tagType.size * length;
32093 // Determine if the value is contained in the dataOffset bytes,
32094 // or if the value at the dataOffset is a pointer to the actual data:
32095 dataOffset = tagSize > 4 ?
32096 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32097 if (dataOffset + tagSize > dataView.byteLength) {
32098 Roo.log('Invalid Exif data: Invalid data offset.');
32101 if (length === 1) {
32102 return tagType.getValue(dataView, dataOffset, littleEndian);
32105 for (i = 0; i < length; i += 1) {
32106 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32109 if (tagType.ascii) {
32111 // Concatenate the chars:
32112 for (i = 0; i < values.length; i += 1) {
32114 // Ignore the terminating NULL byte(s):
32115 if (c === '\u0000') {
32127 Roo.apply(Roo.bootstrap.UploadCropbox, {
32129 'Orientation': 0x0112
32133 1: 0, //'top-left',
32135 3: 180, //'bottom-right',
32136 // 4: 'bottom-left',
32138 6: 90, //'right-top',
32139 // 7: 'right-bottom',
32140 8: 270 //'left-bottom'
32144 // byte, 8-bit unsigned int:
32146 getValue: function (dataView, dataOffset) {
32147 return dataView.getUint8(dataOffset);
32151 // ascii, 8-bit byte:
32153 getValue: function (dataView, dataOffset) {
32154 return String.fromCharCode(dataView.getUint8(dataOffset));
32159 // short, 16 bit int:
32161 getValue: function (dataView, dataOffset, littleEndian) {
32162 return dataView.getUint16(dataOffset, littleEndian);
32166 // long, 32 bit int:
32168 getValue: function (dataView, dataOffset, littleEndian) {
32169 return dataView.getUint32(dataOffset, littleEndian);
32173 // rational = two long values, first is numerator, second is denominator:
32175 getValue: function (dataView, dataOffset, littleEndian) {
32176 return dataView.getUint32(dataOffset, littleEndian) /
32177 dataView.getUint32(dataOffset + 4, littleEndian);
32181 // slong, 32 bit signed int:
32183 getValue: function (dataView, dataOffset, littleEndian) {
32184 return dataView.getInt32(dataOffset, littleEndian);
32188 // srational, two slongs, first is numerator, second is denominator:
32190 getValue: function (dataView, dataOffset, littleEndian) {
32191 return dataView.getInt32(dataOffset, littleEndian) /
32192 dataView.getInt32(dataOffset + 4, littleEndian);
32202 cls : 'btn-group roo-upload-cropbox-rotate-left',
32203 action : 'rotate-left',
32207 cls : 'btn btn-default',
32208 html : '<i class="fa fa-undo"></i>'
32214 cls : 'btn-group roo-upload-cropbox-picture',
32215 action : 'picture',
32219 cls : 'btn btn-default',
32220 html : '<i class="fa fa-picture-o"></i>'
32226 cls : 'btn-group roo-upload-cropbox-rotate-right',
32227 action : 'rotate-right',
32231 cls : 'btn btn-default',
32232 html : '<i class="fa fa-repeat"></i>'
32240 cls : 'btn-group roo-upload-cropbox-rotate-left',
32241 action : 'rotate-left',
32245 cls : 'btn btn-default',
32246 html : '<i class="fa fa-undo"></i>'
32252 cls : 'btn-group roo-upload-cropbox-download',
32253 action : 'download',
32257 cls : 'btn btn-default',
32258 html : '<i class="fa fa-download"></i>'
32264 cls : 'btn-group roo-upload-cropbox-crop',
32269 cls : 'btn btn-default',
32270 html : '<i class="fa fa-crop"></i>'
32276 cls : 'btn-group roo-upload-cropbox-trash',
32281 cls : 'btn btn-default',
32282 html : '<i class="fa fa-trash"></i>'
32288 cls : 'btn-group roo-upload-cropbox-rotate-right',
32289 action : 'rotate-right',
32293 cls : 'btn btn-default',
32294 html : '<i class="fa fa-repeat"></i>'
32302 cls : 'btn-group roo-upload-cropbox-rotate-left',
32303 action : 'rotate-left',
32307 cls : 'btn btn-default',
32308 html : '<i class="fa fa-undo"></i>'
32314 cls : 'btn-group roo-upload-cropbox-rotate-right',
32315 action : 'rotate-right',
32319 cls : 'btn btn-default',
32320 html : '<i class="fa fa-repeat"></i>'
32333 * @class Roo.bootstrap.DocumentManager
32334 * @extends Roo.bootstrap.Component
32335 * Bootstrap DocumentManager class
32336 * @cfg {String} paramName default 'imageUpload'
32337 * @cfg {String} toolTipName default 'filename'
32338 * @cfg {String} method default POST
32339 * @cfg {String} url action url
32340 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32341 * @cfg {Boolean} multiple multiple upload default true
32342 * @cfg {Number} thumbSize default 300
32343 * @cfg {String} fieldLabel
32344 * @cfg {Number} labelWidth default 4
32345 * @cfg {String} labelAlign (left|top) default left
32346 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32347 * @cfg {Number} labellg set the width of label (1-12)
32348 * @cfg {Number} labelmd set the width of label (1-12)
32349 * @cfg {Number} labelsm set the width of label (1-12)
32350 * @cfg {Number} labelxs set the width of label (1-12)
32353 * Create a new DocumentManager
32354 * @param {Object} config The config object
32357 Roo.bootstrap.DocumentManager = function(config){
32358 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32361 this.delegates = [];
32366 * Fire when initial the DocumentManager
32367 * @param {Roo.bootstrap.DocumentManager} this
32372 * inspect selected file
32373 * @param {Roo.bootstrap.DocumentManager} this
32374 * @param {File} file
32379 * Fire when xhr load exception
32380 * @param {Roo.bootstrap.DocumentManager} this
32381 * @param {XMLHttpRequest} xhr
32383 "exception" : true,
32385 * @event afterupload
32386 * Fire when xhr load exception
32387 * @param {Roo.bootstrap.DocumentManager} this
32388 * @param {XMLHttpRequest} xhr
32390 "afterupload" : true,
32393 * prepare the form data
32394 * @param {Roo.bootstrap.DocumentManager} this
32395 * @param {Object} formData
32400 * Fire when remove the file
32401 * @param {Roo.bootstrap.DocumentManager} this
32402 * @param {Object} file
32407 * Fire after refresh the file
32408 * @param {Roo.bootstrap.DocumentManager} this
32413 * Fire after click the image
32414 * @param {Roo.bootstrap.DocumentManager} this
32415 * @param {Object} file
32420 * Fire when upload a image and editable set to true
32421 * @param {Roo.bootstrap.DocumentManager} this
32422 * @param {Object} file
32426 * @event beforeselectfile
32427 * Fire before select file
32428 * @param {Roo.bootstrap.DocumentManager} this
32430 "beforeselectfile" : true,
32433 * Fire before process file
32434 * @param {Roo.bootstrap.DocumentManager} this
32435 * @param {Object} file
32439 * @event previewrendered
32440 * Fire when preview rendered
32441 * @param {Roo.bootstrap.DocumentManager} this
32442 * @param {Object} file
32444 "previewrendered" : true,
32447 "previewResize" : true
32452 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32461 paramName : 'imageUpload',
32462 toolTipName : 'filename',
32465 labelAlign : 'left',
32475 getAutoCreate : function()
32477 var managerWidget = {
32479 cls : 'roo-document-manager',
32483 cls : 'roo-document-manager-selector',
32488 cls : 'roo-document-manager-uploader',
32492 cls : 'roo-document-manager-upload-btn',
32493 html : '<i class="fa fa-plus"></i>'
32504 cls : 'column col-md-12',
32509 if(this.fieldLabel.length){
32514 cls : 'column col-md-12',
32515 html : this.fieldLabel
32519 cls : 'column col-md-12',
32524 if(this.labelAlign == 'left'){
32529 html : this.fieldLabel
32538 if(this.labelWidth > 12){
32539 content[0].style = "width: " + this.labelWidth + 'px';
32542 if(this.labelWidth < 13 && this.labelmd == 0){
32543 this.labelmd = this.labelWidth;
32546 if(this.labellg > 0){
32547 content[0].cls += ' col-lg-' + this.labellg;
32548 content[1].cls += ' col-lg-' + (12 - this.labellg);
32551 if(this.labelmd > 0){
32552 content[0].cls += ' col-md-' + this.labelmd;
32553 content[1].cls += ' col-md-' + (12 - this.labelmd);
32556 if(this.labelsm > 0){
32557 content[0].cls += ' col-sm-' + this.labelsm;
32558 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32561 if(this.labelxs > 0){
32562 content[0].cls += ' col-xs-' + this.labelxs;
32563 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32571 cls : 'row clearfix',
32579 initEvents : function()
32581 this.managerEl = this.el.select('.roo-document-manager', true).first();
32582 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32584 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32585 this.selectorEl.hide();
32588 this.selectorEl.attr('multiple', 'multiple');
32591 this.selectorEl.on('change', this.onFileSelected, this);
32593 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32594 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32596 this.uploader.on('click', this.onUploaderClick, this);
32598 this.renderProgressDialog();
32602 window.addEventListener("resize", function() { _this.refresh(); } );
32604 this.fireEvent('initial', this);
32607 renderProgressDialog : function()
32611 this.progressDialog = new Roo.bootstrap.Modal({
32612 cls : 'roo-document-manager-progress-dialog',
32613 allow_close : false,
32624 btnclick : function() {
32625 _this.uploadCancel();
32631 this.progressDialog.render(Roo.get(document.body));
32633 this.progress = new Roo.bootstrap.Progress({
32634 cls : 'roo-document-manager-progress',
32639 this.progress.render(this.progressDialog.getChildContainer());
32641 this.progressBar = new Roo.bootstrap.ProgressBar({
32642 cls : 'roo-document-manager-progress-bar',
32645 aria_valuemax : 12,
32649 this.progressBar.render(this.progress.getChildContainer());
32652 onUploaderClick : function(e)
32654 e.preventDefault();
32656 if(this.fireEvent('beforeselectfile', this) != false){
32657 this.selectorEl.dom.click();
32662 onFileSelected : function(e)
32664 e.preventDefault();
32666 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32670 Roo.each(this.selectorEl.dom.files, function(file){
32671 if(this.fireEvent('inspect', this, file) != false){
32672 this.files.push(file);
32682 this.selectorEl.dom.value = '';
32684 if(!this.files || !this.files.length){
32688 if(this.boxes > 0 && this.files.length > this.boxes){
32689 this.files = this.files.slice(0, this.boxes);
32692 this.uploader.show();
32694 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32695 this.uploader.hide();
32704 Roo.each(this.files, function(file){
32706 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32707 var f = this.renderPreview(file);
32712 if(file.type.indexOf('image') != -1){
32713 this.delegates.push(
32715 _this.process(file);
32716 }).createDelegate(this)
32724 _this.process(file);
32725 }).createDelegate(this)
32730 this.files = files;
32732 this.delegates = this.delegates.concat(docs);
32734 if(!this.delegates.length){
32739 this.progressBar.aria_valuemax = this.delegates.length;
32746 arrange : function()
32748 if(!this.delegates.length){
32749 this.progressDialog.hide();
32754 var delegate = this.delegates.shift();
32756 this.progressDialog.show();
32758 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32760 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32765 refresh : function()
32767 this.uploader.show();
32769 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32770 this.uploader.hide();
32773 Roo.isTouch ? this.closable(false) : this.closable(true);
32775 this.fireEvent('refresh', this);
32778 onRemove : function(e, el, o)
32780 e.preventDefault();
32782 this.fireEvent('remove', this, o);
32786 remove : function(o)
32790 Roo.each(this.files, function(file){
32791 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32800 this.files = files;
32807 Roo.each(this.files, function(file){
32812 file.target.remove();
32821 onClick : function(e, el, o)
32823 e.preventDefault();
32825 this.fireEvent('click', this, o);
32829 closable : function(closable)
32831 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32833 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32845 xhrOnLoad : function(xhr)
32847 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32851 if (xhr.readyState !== 4) {
32853 this.fireEvent('exception', this, xhr);
32857 var response = Roo.decode(xhr.responseText);
32859 if(!response.success){
32861 this.fireEvent('exception', this, xhr);
32865 var file = this.renderPreview(response.data);
32867 this.files.push(file);
32871 this.fireEvent('afterupload', this, xhr);
32875 xhrOnError : function(xhr)
32877 Roo.log('xhr on error');
32879 var response = Roo.decode(xhr.responseText);
32886 process : function(file)
32888 if(this.fireEvent('process', this, file) !== false){
32889 if(this.editable && file.type.indexOf('image') != -1){
32890 this.fireEvent('edit', this, file);
32894 this.uploadStart(file, false);
32901 uploadStart : function(file, crop)
32903 this.xhr = new XMLHttpRequest();
32905 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32910 file.xhr = this.xhr;
32912 this.managerEl.createChild({
32914 cls : 'roo-document-manager-loading',
32918 tooltip : file.name,
32919 cls : 'roo-document-manager-thumb',
32920 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32926 this.xhr.open(this.method, this.url, true);
32929 "Accept": "application/json",
32930 "Cache-Control": "no-cache",
32931 "X-Requested-With": "XMLHttpRequest"
32934 for (var headerName in headers) {
32935 var headerValue = headers[headerName];
32937 this.xhr.setRequestHeader(headerName, headerValue);
32943 this.xhr.onload = function()
32945 _this.xhrOnLoad(_this.xhr);
32948 this.xhr.onerror = function()
32950 _this.xhrOnError(_this.xhr);
32953 var formData = new FormData();
32955 formData.append('returnHTML', 'NO');
32958 formData.append('crop', crop);
32961 formData.append(this.paramName, file, file.name);
32968 if(this.fireEvent('prepare', this, formData, options) != false){
32970 if(options.manually){
32974 this.xhr.send(formData);
32978 this.uploadCancel();
32981 uploadCancel : function()
32987 this.delegates = [];
32989 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32996 renderPreview : function(file)
32998 if(typeof(file.target) != 'undefined' && file.target){
33002 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33004 var previewEl = this.managerEl.createChild({
33006 cls : 'roo-document-manager-preview',
33010 tooltip : file[this.toolTipName],
33011 cls : 'roo-document-manager-thumb',
33012 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33017 html : '<i class="fa fa-times-circle"></i>'
33022 var close = previewEl.select('button.close', true).first();
33024 close.on('click', this.onRemove, this, file);
33026 file.target = previewEl;
33028 var image = previewEl.select('img', true).first();
33032 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33034 image.on('click', this.onClick, this, file);
33036 this.fireEvent('previewrendered', this, file);
33042 onPreviewLoad : function(file, image)
33044 if(typeof(file.target) == 'undefined' || !file.target){
33048 var width = image.dom.naturalWidth || image.dom.width;
33049 var height = image.dom.naturalHeight || image.dom.height;
33051 if(!this.previewResize) {
33055 if(width > height){
33056 file.target.addClass('wide');
33060 file.target.addClass('tall');
33065 uploadFromSource : function(file, crop)
33067 this.xhr = new XMLHttpRequest();
33069 this.managerEl.createChild({
33071 cls : 'roo-document-manager-loading',
33075 tooltip : file.name,
33076 cls : 'roo-document-manager-thumb',
33077 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33083 this.xhr.open(this.method, this.url, true);
33086 "Accept": "application/json",
33087 "Cache-Control": "no-cache",
33088 "X-Requested-With": "XMLHttpRequest"
33091 for (var headerName in headers) {
33092 var headerValue = headers[headerName];
33094 this.xhr.setRequestHeader(headerName, headerValue);
33100 this.xhr.onload = function()
33102 _this.xhrOnLoad(_this.xhr);
33105 this.xhr.onerror = function()
33107 _this.xhrOnError(_this.xhr);
33110 var formData = new FormData();
33112 formData.append('returnHTML', 'NO');
33114 formData.append('crop', crop);
33116 if(typeof(file.filename) != 'undefined'){
33117 formData.append('filename', file.filename);
33120 if(typeof(file.mimetype) != 'undefined'){
33121 formData.append('mimetype', file.mimetype);
33126 if(this.fireEvent('prepare', this, formData) != false){
33127 this.xhr.send(formData);
33137 * @class Roo.bootstrap.DocumentViewer
33138 * @extends Roo.bootstrap.Component
33139 * Bootstrap DocumentViewer class
33140 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33141 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33144 * Create a new DocumentViewer
33145 * @param {Object} config The config object
33148 Roo.bootstrap.DocumentViewer = function(config){
33149 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33154 * Fire after initEvent
33155 * @param {Roo.bootstrap.DocumentViewer} this
33161 * @param {Roo.bootstrap.DocumentViewer} this
33166 * Fire after download button
33167 * @param {Roo.bootstrap.DocumentViewer} this
33172 * Fire after trash button
33173 * @param {Roo.bootstrap.DocumentViewer} this
33180 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33182 showDownload : true,
33186 getAutoCreate : function()
33190 cls : 'roo-document-viewer',
33194 cls : 'roo-document-viewer-body',
33198 cls : 'roo-document-viewer-thumb',
33202 cls : 'roo-document-viewer-image'
33210 cls : 'roo-document-viewer-footer',
33213 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33217 cls : 'btn-group roo-document-viewer-download',
33221 cls : 'btn btn-default',
33222 html : '<i class="fa fa-download"></i>'
33228 cls : 'btn-group roo-document-viewer-trash',
33232 cls : 'btn btn-default',
33233 html : '<i class="fa fa-trash"></i>'
33246 initEvents : function()
33248 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33249 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33251 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33252 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33254 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33255 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33257 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33258 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33260 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33261 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33263 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33264 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33266 this.bodyEl.on('click', this.onClick, this);
33267 this.downloadBtn.on('click', this.onDownload, this);
33268 this.trashBtn.on('click', this.onTrash, this);
33270 this.downloadBtn.hide();
33271 this.trashBtn.hide();
33273 if(this.showDownload){
33274 this.downloadBtn.show();
33277 if(this.showTrash){
33278 this.trashBtn.show();
33281 if(!this.showDownload && !this.showTrash) {
33282 this.footerEl.hide();
33287 initial : function()
33289 this.fireEvent('initial', this);
33293 onClick : function(e)
33295 e.preventDefault();
33297 this.fireEvent('click', this);
33300 onDownload : function(e)
33302 e.preventDefault();
33304 this.fireEvent('download', this);
33307 onTrash : function(e)
33309 e.preventDefault();
33311 this.fireEvent('trash', this);
33323 * @class Roo.bootstrap.NavProgressBar
33324 * @extends Roo.bootstrap.Component
33325 * Bootstrap NavProgressBar class
33328 * Create a new nav progress bar
33329 * @param {Object} config The config object
33332 Roo.bootstrap.NavProgressBar = function(config){
33333 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33335 this.bullets = this.bullets || [];
33337 // Roo.bootstrap.NavProgressBar.register(this);
33341 * Fires when the active item changes
33342 * @param {Roo.bootstrap.NavProgressBar} this
33343 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33344 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33351 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33356 getAutoCreate : function()
33358 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33362 cls : 'roo-navigation-bar-group',
33366 cls : 'roo-navigation-top-bar'
33370 cls : 'roo-navigation-bullets-bar',
33374 cls : 'roo-navigation-bar'
33381 cls : 'roo-navigation-bottom-bar'
33391 initEvents: function()
33396 onRender : function(ct, position)
33398 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33400 if(this.bullets.length){
33401 Roo.each(this.bullets, function(b){
33410 addItem : function(cfg)
33412 var item = new Roo.bootstrap.NavProgressItem(cfg);
33414 item.parentId = this.id;
33415 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33418 var top = new Roo.bootstrap.Element({
33420 cls : 'roo-navigation-bar-text'
33423 var bottom = new Roo.bootstrap.Element({
33425 cls : 'roo-navigation-bar-text'
33428 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33429 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33431 var topText = new Roo.bootstrap.Element({
33433 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33436 var bottomText = new Roo.bootstrap.Element({
33438 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33441 topText.onRender(top.el, null);
33442 bottomText.onRender(bottom.el, null);
33445 item.bottomEl = bottom;
33448 this.barItems.push(item);
33453 getActive : function()
33455 var active = false;
33457 Roo.each(this.barItems, function(v){
33459 if (!v.isActive()) {
33471 setActiveItem : function(item)
33475 Roo.each(this.barItems, function(v){
33476 if (v.rid == item.rid) {
33480 if (v.isActive()) {
33481 v.setActive(false);
33486 item.setActive(true);
33488 this.fireEvent('changed', this, item, prev);
33491 getBarItem: function(rid)
33495 Roo.each(this.barItems, function(e) {
33496 if (e.rid != rid) {
33507 indexOfItem : function(item)
33511 Roo.each(this.barItems, function(v, i){
33513 if (v.rid != item.rid) {
33524 setActiveNext : function()
33526 var i = this.indexOfItem(this.getActive());
33528 if (i > this.barItems.length) {
33532 this.setActiveItem(this.barItems[i+1]);
33535 setActivePrev : function()
33537 var i = this.indexOfItem(this.getActive());
33543 this.setActiveItem(this.barItems[i-1]);
33546 format : function()
33548 if(!this.barItems.length){
33552 var width = 100 / this.barItems.length;
33554 Roo.each(this.barItems, function(i){
33555 i.el.setStyle('width', width + '%');
33556 i.topEl.el.setStyle('width', width + '%');
33557 i.bottomEl.el.setStyle('width', width + '%');
33566 * Nav Progress Item
33571 * @class Roo.bootstrap.NavProgressItem
33572 * @extends Roo.bootstrap.Component
33573 * Bootstrap NavProgressItem class
33574 * @cfg {String} rid the reference id
33575 * @cfg {Boolean} active (true|false) Is item active default false
33576 * @cfg {Boolean} disabled (true|false) Is item active default false
33577 * @cfg {String} html
33578 * @cfg {String} position (top|bottom) text position default bottom
33579 * @cfg {String} icon show icon instead of number
33582 * Create a new NavProgressItem
33583 * @param {Object} config The config object
33585 Roo.bootstrap.NavProgressItem = function(config){
33586 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33591 * The raw click event for the entire grid.
33592 * @param {Roo.bootstrap.NavProgressItem} this
33593 * @param {Roo.EventObject} e
33600 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33606 position : 'bottom',
33609 getAutoCreate : function()
33611 var iconCls = 'roo-navigation-bar-item-icon';
33613 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33617 cls: 'roo-navigation-bar-item',
33627 cfg.cls += ' active';
33630 cfg.cls += ' disabled';
33636 disable : function()
33638 this.setDisabled(true);
33641 enable : function()
33643 this.setDisabled(false);
33646 initEvents: function()
33648 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33650 this.iconEl.on('click', this.onClick, this);
33653 onClick : function(e)
33655 e.preventDefault();
33661 if(this.fireEvent('click', this, e) === false){
33665 this.parent().setActiveItem(this);
33668 isActive: function ()
33670 return this.active;
33673 setActive : function(state)
33675 if(this.active == state){
33679 this.active = state;
33682 this.el.addClass('active');
33686 this.el.removeClass('active');
33691 setDisabled : function(state)
33693 if(this.disabled == state){
33697 this.disabled = state;
33700 this.el.addClass('disabled');
33704 this.el.removeClass('disabled');
33707 tooltipEl : function()
33709 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33722 * @class Roo.bootstrap.FieldLabel
33723 * @extends Roo.bootstrap.Component
33724 * Bootstrap FieldLabel class
33725 * @cfg {String} html contents of the element
33726 * @cfg {String} tag tag of the element default label
33727 * @cfg {String} cls class of the element
33728 * @cfg {String} target label target
33729 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33730 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33731 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33732 * @cfg {String} iconTooltip default "This field is required"
33733 * @cfg {String} indicatorpos (left|right) default left
33736 * Create a new FieldLabel
33737 * @param {Object} config The config object
33740 Roo.bootstrap.FieldLabel = function(config){
33741 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33746 * Fires after the field has been marked as invalid.
33747 * @param {Roo.form.FieldLabel} this
33748 * @param {String} msg The validation message
33753 * Fires after the field has been validated with no errors.
33754 * @param {Roo.form.FieldLabel} this
33760 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33767 invalidClass : 'has-warning',
33768 validClass : 'has-success',
33769 iconTooltip : 'This field is required',
33770 indicatorpos : 'left',
33772 getAutoCreate : function(){
33775 if (!this.allowBlank) {
33781 cls : 'roo-bootstrap-field-label ' + this.cls,
33786 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33787 tooltip : this.iconTooltip
33796 if(this.indicatorpos == 'right'){
33799 cls : 'roo-bootstrap-field-label ' + this.cls,
33808 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33809 tooltip : this.iconTooltip
33818 initEvents: function()
33820 Roo.bootstrap.Element.superclass.initEvents.call(this);
33822 this.indicator = this.indicatorEl();
33824 if(this.indicator){
33825 this.indicator.removeClass('visible');
33826 this.indicator.addClass('invisible');
33829 Roo.bootstrap.FieldLabel.register(this);
33832 indicatorEl : function()
33834 var indicator = this.el.select('i.roo-required-indicator',true).first();
33845 * Mark this field as valid
33847 markValid : function()
33849 if(this.indicator){
33850 this.indicator.removeClass('visible');
33851 this.indicator.addClass('invisible');
33853 if (Roo.bootstrap.version == 3) {
33854 this.el.removeClass(this.invalidClass);
33855 this.el.addClass(this.validClass);
33857 this.el.removeClass('is-invalid');
33858 this.el.addClass('is-valid');
33862 this.fireEvent('valid', this);
33866 * Mark this field as invalid
33867 * @param {String} msg The validation message
33869 markInvalid : function(msg)
33871 if(this.indicator){
33872 this.indicator.removeClass('invisible');
33873 this.indicator.addClass('visible');
33875 if (Roo.bootstrap.version == 3) {
33876 this.el.removeClass(this.validClass);
33877 this.el.addClass(this.invalidClass);
33879 this.el.removeClass('is-valid');
33880 this.el.addClass('is-invalid');
33884 this.fireEvent('invalid', this, msg);
33890 Roo.apply(Roo.bootstrap.FieldLabel, {
33895 * register a FieldLabel Group
33896 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33898 register : function(label)
33900 if(this.groups.hasOwnProperty(label.target)){
33904 this.groups[label.target] = label;
33908 * fetch a FieldLabel Group based on the target
33909 * @param {string} target
33910 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33912 get: function(target) {
33913 if (typeof(this.groups[target]) == 'undefined') {
33917 return this.groups[target] ;
33926 * page DateSplitField.
33932 * @class Roo.bootstrap.DateSplitField
33933 * @extends Roo.bootstrap.Component
33934 * Bootstrap DateSplitField class
33935 * @cfg {string} fieldLabel - the label associated
33936 * @cfg {Number} labelWidth set the width of label (0-12)
33937 * @cfg {String} labelAlign (top|left)
33938 * @cfg {Boolean} dayAllowBlank (true|false) default false
33939 * @cfg {Boolean} monthAllowBlank (true|false) default false
33940 * @cfg {Boolean} yearAllowBlank (true|false) default false
33941 * @cfg {string} dayPlaceholder
33942 * @cfg {string} monthPlaceholder
33943 * @cfg {string} yearPlaceholder
33944 * @cfg {string} dayFormat default 'd'
33945 * @cfg {string} monthFormat default 'm'
33946 * @cfg {string} yearFormat default 'Y'
33947 * @cfg {Number} labellg set the width of label (1-12)
33948 * @cfg {Number} labelmd set the width of label (1-12)
33949 * @cfg {Number} labelsm set the width of label (1-12)
33950 * @cfg {Number} labelxs set the width of label (1-12)
33954 * Create a new DateSplitField
33955 * @param {Object} config The config object
33958 Roo.bootstrap.DateSplitField = function(config){
33959 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33965 * getting the data of years
33966 * @param {Roo.bootstrap.DateSplitField} this
33967 * @param {Object} years
33972 * getting the data of days
33973 * @param {Roo.bootstrap.DateSplitField} this
33974 * @param {Object} days
33979 * Fires after the field has been marked as invalid.
33980 * @param {Roo.form.Field} this
33981 * @param {String} msg The validation message
33986 * Fires after the field has been validated with no errors.
33987 * @param {Roo.form.Field} this
33993 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33996 labelAlign : 'top',
33998 dayAllowBlank : false,
33999 monthAllowBlank : false,
34000 yearAllowBlank : false,
34001 dayPlaceholder : '',
34002 monthPlaceholder : '',
34003 yearPlaceholder : '',
34007 isFormField : true,
34013 getAutoCreate : function()
34017 cls : 'row roo-date-split-field-group',
34022 cls : 'form-hidden-field roo-date-split-field-group-value',
34028 var labelCls = 'col-md-12';
34029 var contentCls = 'col-md-4';
34031 if(this.fieldLabel){
34035 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34039 html : this.fieldLabel
34044 if(this.labelAlign == 'left'){
34046 if(this.labelWidth > 12){
34047 label.style = "width: " + this.labelWidth + 'px';
34050 if(this.labelWidth < 13 && this.labelmd == 0){
34051 this.labelmd = this.labelWidth;
34054 if(this.labellg > 0){
34055 labelCls = ' col-lg-' + this.labellg;
34056 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34059 if(this.labelmd > 0){
34060 labelCls = ' col-md-' + this.labelmd;
34061 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34064 if(this.labelsm > 0){
34065 labelCls = ' col-sm-' + this.labelsm;
34066 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34069 if(this.labelxs > 0){
34070 labelCls = ' col-xs-' + this.labelxs;
34071 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34075 label.cls += ' ' + labelCls;
34077 cfg.cn.push(label);
34080 Roo.each(['day', 'month', 'year'], function(t){
34083 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34090 inputEl: function ()
34092 return this.el.select('.roo-date-split-field-group-value', true).first();
34095 onRender : function(ct, position)
34099 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34101 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34103 this.dayField = new Roo.bootstrap.ComboBox({
34104 allowBlank : this.dayAllowBlank,
34105 alwaysQuery : true,
34106 displayField : 'value',
34109 forceSelection : true,
34111 placeholder : this.dayPlaceholder,
34112 selectOnFocus : true,
34113 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34114 triggerAction : 'all',
34116 valueField : 'value',
34117 store : new Roo.data.SimpleStore({
34118 data : (function() {
34120 _this.fireEvent('days', _this, days);
34123 fields : [ 'value' ]
34126 select : function (_self, record, index)
34128 _this.setValue(_this.getValue());
34133 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34135 this.monthField = new Roo.bootstrap.MonthField({
34136 after : '<i class=\"fa fa-calendar\"></i>',
34137 allowBlank : this.monthAllowBlank,
34138 placeholder : this.monthPlaceholder,
34141 render : function (_self)
34143 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34144 e.preventDefault();
34148 select : function (_self, oldvalue, newvalue)
34150 _this.setValue(_this.getValue());
34155 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34157 this.yearField = new Roo.bootstrap.ComboBox({
34158 allowBlank : this.yearAllowBlank,
34159 alwaysQuery : true,
34160 displayField : 'value',
34163 forceSelection : true,
34165 placeholder : this.yearPlaceholder,
34166 selectOnFocus : true,
34167 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34168 triggerAction : 'all',
34170 valueField : 'value',
34171 store : new Roo.data.SimpleStore({
34172 data : (function() {
34174 _this.fireEvent('years', _this, years);
34177 fields : [ 'value' ]
34180 select : function (_self, record, index)
34182 _this.setValue(_this.getValue());
34187 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34190 setValue : function(v, format)
34192 this.inputEl.dom.value = v;
34194 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34196 var d = Date.parseDate(v, f);
34203 this.setDay(d.format(this.dayFormat));
34204 this.setMonth(d.format(this.monthFormat));
34205 this.setYear(d.format(this.yearFormat));
34212 setDay : function(v)
34214 this.dayField.setValue(v);
34215 this.inputEl.dom.value = this.getValue();
34220 setMonth : function(v)
34222 this.monthField.setValue(v, true);
34223 this.inputEl.dom.value = this.getValue();
34228 setYear : function(v)
34230 this.yearField.setValue(v);
34231 this.inputEl.dom.value = this.getValue();
34236 getDay : function()
34238 return this.dayField.getValue();
34241 getMonth : function()
34243 return this.monthField.getValue();
34246 getYear : function()
34248 return this.yearField.getValue();
34251 getValue : function()
34253 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34255 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34265 this.inputEl.dom.value = '';
34270 validate : function()
34272 var d = this.dayField.validate();
34273 var m = this.monthField.validate();
34274 var y = this.yearField.validate();
34279 (!this.dayAllowBlank && !d) ||
34280 (!this.monthAllowBlank && !m) ||
34281 (!this.yearAllowBlank && !y)
34286 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34295 this.markInvalid();
34300 markValid : function()
34303 var label = this.el.select('label', true).first();
34304 var icon = this.el.select('i.fa-star', true).first();
34310 this.fireEvent('valid', this);
34314 * Mark this field as invalid
34315 * @param {String} msg The validation message
34317 markInvalid : function(msg)
34320 var label = this.el.select('label', true).first();
34321 var icon = this.el.select('i.fa-star', true).first();
34323 if(label && !icon){
34324 this.el.select('.roo-date-split-field-label', true).createChild({
34326 cls : 'text-danger fa fa-lg fa-star',
34327 tooltip : 'This field is required',
34328 style : 'margin-right:5px;'
34332 this.fireEvent('invalid', this, msg);
34335 clearInvalid : function()
34337 var label = this.el.select('label', true).first();
34338 var icon = this.el.select('i.fa-star', true).first();
34344 this.fireEvent('valid', this);
34347 getName: function()
34357 * http://masonry.desandro.com
34359 * The idea is to render all the bricks based on vertical width...
34361 * The original code extends 'outlayer' - we might need to use that....
34367 * @class Roo.bootstrap.LayoutMasonry
34368 * @extends Roo.bootstrap.Component
34369 * Bootstrap Layout Masonry class
34372 * Create a new Element
34373 * @param {Object} config The config object
34376 Roo.bootstrap.LayoutMasonry = function(config){
34378 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34382 Roo.bootstrap.LayoutMasonry.register(this);
34388 * Fire after layout the items
34389 * @param {Roo.bootstrap.LayoutMasonry} this
34390 * @param {Roo.EventObject} e
34397 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34400 * @cfg {Boolean} isLayoutInstant = no animation?
34402 isLayoutInstant : false, // needed?
34405 * @cfg {Number} boxWidth width of the columns
34410 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34415 * @cfg {Number} padWidth padding below box..
34420 * @cfg {Number} gutter gutter width..
34425 * @cfg {Number} maxCols maximum number of columns
34431 * @cfg {Boolean} isAutoInitial defalut true
34433 isAutoInitial : true,
34438 * @cfg {Boolean} isHorizontal defalut false
34440 isHorizontal : false,
34442 currentSize : null,
34448 bricks: null, //CompositeElement
34452 _isLayoutInited : false,
34454 // isAlternative : false, // only use for vertical layout...
34457 * @cfg {Number} alternativePadWidth padding below box..
34459 alternativePadWidth : 50,
34461 selectedBrick : [],
34463 getAutoCreate : function(){
34465 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34469 cls: 'blog-masonary-wrapper ' + this.cls,
34471 cls : 'mas-boxes masonary'
34478 getChildContainer: function( )
34480 if (this.boxesEl) {
34481 return this.boxesEl;
34484 this.boxesEl = this.el.select('.mas-boxes').first();
34486 return this.boxesEl;
34490 initEvents : function()
34494 if(this.isAutoInitial){
34495 Roo.log('hook children rendered');
34496 this.on('childrenrendered', function() {
34497 Roo.log('children rendered');
34503 initial : function()
34505 this.selectedBrick = [];
34507 this.currentSize = this.el.getBox(true);
34509 Roo.EventManager.onWindowResize(this.resize, this);
34511 if(!this.isAutoInitial){
34519 //this.layout.defer(500,this);
34523 resize : function()
34525 var cs = this.el.getBox(true);
34528 this.currentSize.width == cs.width &&
34529 this.currentSize.x == cs.x &&
34530 this.currentSize.height == cs.height &&
34531 this.currentSize.y == cs.y
34533 Roo.log("no change in with or X or Y");
34537 this.currentSize = cs;
34543 layout : function()
34545 this._resetLayout();
34547 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34549 this.layoutItems( isInstant );
34551 this._isLayoutInited = true;
34553 this.fireEvent('layout', this);
34557 _resetLayout : function()
34559 if(this.isHorizontal){
34560 this.horizontalMeasureColumns();
34564 this.verticalMeasureColumns();
34568 verticalMeasureColumns : function()
34570 this.getContainerWidth();
34572 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34573 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34577 var boxWidth = this.boxWidth + this.padWidth;
34579 if(this.containerWidth < this.boxWidth){
34580 boxWidth = this.containerWidth
34583 var containerWidth = this.containerWidth;
34585 var cols = Math.floor(containerWidth / boxWidth);
34587 this.cols = Math.max( cols, 1 );
34589 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34591 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34593 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34595 this.colWidth = boxWidth + avail - this.padWidth;
34597 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34598 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34601 horizontalMeasureColumns : function()
34603 this.getContainerWidth();
34605 var boxWidth = this.boxWidth;
34607 if(this.containerWidth < boxWidth){
34608 boxWidth = this.containerWidth;
34611 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34613 this.el.setHeight(boxWidth);
34617 getContainerWidth : function()
34619 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34622 layoutItems : function( isInstant )
34624 Roo.log(this.bricks);
34626 var items = Roo.apply([], this.bricks);
34628 if(this.isHorizontal){
34629 this._horizontalLayoutItems( items , isInstant );
34633 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34634 // this._verticalAlternativeLayoutItems( items , isInstant );
34638 this._verticalLayoutItems( items , isInstant );
34642 _verticalLayoutItems : function ( items , isInstant)
34644 if ( !items || !items.length ) {
34649 ['xs', 'xs', 'xs', 'tall'],
34650 ['xs', 'xs', 'tall'],
34651 ['xs', 'xs', 'sm'],
34652 ['xs', 'xs', 'xs'],
34658 ['sm', 'xs', 'xs'],
34662 ['tall', 'xs', 'xs', 'xs'],
34663 ['tall', 'xs', 'xs'],
34675 Roo.each(items, function(item, k){
34677 switch (item.size) {
34678 // these layouts take up a full box,
34689 boxes.push([item]);
34712 var filterPattern = function(box, length)
34720 var pattern = box.slice(0, length);
34724 Roo.each(pattern, function(i){
34725 format.push(i.size);
34728 Roo.each(standard, function(s){
34730 if(String(s) != String(format)){
34739 if(!match && length == 1){
34744 filterPattern(box, length - 1);
34748 queue.push(pattern);
34750 box = box.slice(length, box.length);
34752 filterPattern(box, 4);
34758 Roo.each(boxes, function(box, k){
34764 if(box.length == 1){
34769 filterPattern(box, 4);
34773 this._processVerticalLayoutQueue( queue, isInstant );
34777 // _verticalAlternativeLayoutItems : function( items , isInstant )
34779 // if ( !items || !items.length ) {
34783 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34787 _horizontalLayoutItems : function ( items , isInstant)
34789 if ( !items || !items.length || items.length < 3) {
34795 var eItems = items.slice(0, 3);
34797 items = items.slice(3, items.length);
34800 ['xs', 'xs', 'xs', 'wide'],
34801 ['xs', 'xs', 'wide'],
34802 ['xs', 'xs', 'sm'],
34803 ['xs', 'xs', 'xs'],
34809 ['sm', 'xs', 'xs'],
34813 ['wide', 'xs', 'xs', 'xs'],
34814 ['wide', 'xs', 'xs'],
34827 Roo.each(items, function(item, k){
34829 switch (item.size) {
34840 boxes.push([item]);
34864 var filterPattern = function(box, length)
34872 var pattern = box.slice(0, length);
34876 Roo.each(pattern, function(i){
34877 format.push(i.size);
34880 Roo.each(standard, function(s){
34882 if(String(s) != String(format)){
34891 if(!match && length == 1){
34896 filterPattern(box, length - 1);
34900 queue.push(pattern);
34902 box = box.slice(length, box.length);
34904 filterPattern(box, 4);
34910 Roo.each(boxes, function(box, k){
34916 if(box.length == 1){
34921 filterPattern(box, 4);
34928 var pos = this.el.getBox(true);
34932 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34934 var hit_end = false;
34936 Roo.each(queue, function(box){
34940 Roo.each(box, function(b){
34942 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34952 Roo.each(box, function(b){
34954 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34957 mx = Math.max(mx, b.x);
34961 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34965 Roo.each(box, function(b){
34967 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34981 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34984 /** Sets position of item in DOM
34985 * @param {Element} item
34986 * @param {Number} x - horizontal position
34987 * @param {Number} y - vertical position
34988 * @param {Boolean} isInstant - disables transitions
34990 _processVerticalLayoutQueue : function( queue, isInstant )
34992 var pos = this.el.getBox(true);
34997 for (var i = 0; i < this.cols; i++){
35001 Roo.each(queue, function(box, k){
35003 var col = k % this.cols;
35005 Roo.each(box, function(b,kk){
35007 b.el.position('absolute');
35009 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35010 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35012 if(b.size == 'md-left' || b.size == 'md-right'){
35013 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35014 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35017 b.el.setWidth(width);
35018 b.el.setHeight(height);
35020 b.el.select('iframe',true).setSize(width,height);
35024 for (var i = 0; i < this.cols; i++){
35026 if(maxY[i] < maxY[col]){
35031 col = Math.min(col, i);
35035 x = pos.x + col * (this.colWidth + this.padWidth);
35039 var positions = [];
35041 switch (box.length){
35043 positions = this.getVerticalOneBoxColPositions(x, y, box);
35046 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35049 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35052 positions = this.getVerticalFourBoxColPositions(x, y, box);
35058 Roo.each(box, function(b,kk){
35060 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35062 var sz = b.el.getSize();
35064 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35072 for (var i = 0; i < this.cols; i++){
35073 mY = Math.max(mY, maxY[i]);
35076 this.el.setHeight(mY - pos.y);
35080 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35082 // var pos = this.el.getBox(true);
35085 // var maxX = pos.right;
35087 // var maxHeight = 0;
35089 // Roo.each(items, function(item, k){
35093 // item.el.position('absolute');
35095 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35097 // item.el.setWidth(width);
35099 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35101 // item.el.setHeight(height);
35104 // item.el.setXY([x, y], isInstant ? false : true);
35106 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35109 // y = y + height + this.alternativePadWidth;
35111 // maxHeight = maxHeight + height + this.alternativePadWidth;
35115 // this.el.setHeight(maxHeight);
35119 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35121 var pos = this.el.getBox(true);
35126 var maxX = pos.right;
35128 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35130 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35132 Roo.each(queue, function(box, k){
35134 Roo.each(box, function(b, kk){
35136 b.el.position('absolute');
35138 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35139 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35141 if(b.size == 'md-left' || b.size == 'md-right'){
35142 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35143 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35146 b.el.setWidth(width);
35147 b.el.setHeight(height);
35155 var positions = [];
35157 switch (box.length){
35159 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35162 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35165 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35168 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35174 Roo.each(box, function(b,kk){
35176 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35178 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35186 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35188 Roo.each(eItems, function(b,k){
35190 b.size = (k == 0) ? 'sm' : 'xs';
35191 b.x = (k == 0) ? 2 : 1;
35192 b.y = (k == 0) ? 2 : 1;
35194 b.el.position('absolute');
35196 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35198 b.el.setWidth(width);
35200 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35202 b.el.setHeight(height);
35206 var positions = [];
35209 x : maxX - this.unitWidth * 2 - this.gutter,
35214 x : maxX - this.unitWidth,
35215 y : minY + (this.unitWidth + this.gutter) * 2
35219 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35223 Roo.each(eItems, function(b,k){
35225 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35231 getVerticalOneBoxColPositions : function(x, y, box)
35235 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35237 if(box[0].size == 'md-left'){
35241 if(box[0].size == 'md-right'){
35246 x : x + (this.unitWidth + this.gutter) * rand,
35253 getVerticalTwoBoxColPositions : function(x, y, box)
35257 if(box[0].size == 'xs'){
35261 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35265 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35279 x : x + (this.unitWidth + this.gutter) * 2,
35280 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35287 getVerticalThreeBoxColPositions : function(x, y, box)
35291 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35299 x : x + (this.unitWidth + this.gutter) * 1,
35304 x : x + (this.unitWidth + this.gutter) * 2,
35312 if(box[0].size == 'xs' && box[1].size == 'xs'){
35321 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35325 x : x + (this.unitWidth + this.gutter) * 1,
35339 x : x + (this.unitWidth + this.gutter) * 2,
35344 x : x + (this.unitWidth + this.gutter) * 2,
35345 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35352 getVerticalFourBoxColPositions : function(x, y, box)
35356 if(box[0].size == 'xs'){
35365 y : y + (this.unitHeight + this.gutter) * 1
35370 y : y + (this.unitHeight + this.gutter) * 2
35374 x : x + (this.unitWidth + this.gutter) * 1,
35388 x : x + (this.unitWidth + this.gutter) * 2,
35393 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35394 y : y + (this.unitHeight + this.gutter) * 1
35398 x : x + (this.unitWidth + this.gutter) * 2,
35399 y : y + (this.unitWidth + this.gutter) * 2
35406 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35410 if(box[0].size == 'md-left'){
35412 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35419 if(box[0].size == 'md-right'){
35421 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35422 y : minY + (this.unitWidth + this.gutter) * 1
35428 var rand = Math.floor(Math.random() * (4 - box[0].y));
35431 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35432 y : minY + (this.unitWidth + this.gutter) * rand
35439 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35443 if(box[0].size == 'xs'){
35446 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35451 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35452 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35460 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35465 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35466 y : minY + (this.unitWidth + this.gutter) * 2
35473 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35477 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35480 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35485 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35486 y : minY + (this.unitWidth + this.gutter) * 1
35490 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35491 y : minY + (this.unitWidth + this.gutter) * 2
35498 if(box[0].size == 'xs' && box[1].size == 'xs'){
35501 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35506 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35511 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35512 y : minY + (this.unitWidth + this.gutter) * 1
35520 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35525 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35526 y : minY + (this.unitWidth + this.gutter) * 2
35530 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35531 y : minY + (this.unitWidth + this.gutter) * 2
35538 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35542 if(box[0].size == 'xs'){
35545 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35550 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35555 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),
35560 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35561 y : minY + (this.unitWidth + this.gutter) * 1
35569 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35574 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35575 y : minY + (this.unitWidth + this.gutter) * 2
35579 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35580 y : minY + (this.unitWidth + this.gutter) * 2
35584 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),
35585 y : minY + (this.unitWidth + this.gutter) * 2
35593 * remove a Masonry Brick
35594 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35596 removeBrick : function(brick_id)
35602 for (var i = 0; i<this.bricks.length; i++) {
35603 if (this.bricks[i].id == brick_id) {
35604 this.bricks.splice(i,1);
35605 this.el.dom.removeChild(Roo.get(brick_id).dom);
35612 * adds a Masonry Brick
35613 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35615 addBrick : function(cfg)
35617 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35618 //this.register(cn);
35619 cn.parentId = this.id;
35620 cn.render(this.el);
35625 * register a Masonry Brick
35626 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35629 register : function(brick)
35631 this.bricks.push(brick);
35632 brick.masonryId = this.id;
35636 * clear all the Masonry Brick
35638 clearAll : function()
35641 //this.getChildContainer().dom.innerHTML = "";
35642 this.el.dom.innerHTML = '';
35645 getSelected : function()
35647 if (!this.selectedBrick) {
35651 return this.selectedBrick;
35655 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35659 * register a Masonry Layout
35660 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35663 register : function(layout)
35665 this.groups[layout.id] = layout;
35668 * fetch a Masonry Layout based on the masonry layout ID
35669 * @param {string} the masonry layout to add
35670 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35673 get: function(layout_id) {
35674 if (typeof(this.groups[layout_id]) == 'undefined') {
35677 return this.groups[layout_id] ;
35689 * http://masonry.desandro.com
35691 * The idea is to render all the bricks based on vertical width...
35693 * The original code extends 'outlayer' - we might need to use that....
35699 * @class Roo.bootstrap.LayoutMasonryAuto
35700 * @extends Roo.bootstrap.Component
35701 * Bootstrap Layout Masonry class
35704 * Create a new Element
35705 * @param {Object} config The config object
35708 Roo.bootstrap.LayoutMasonryAuto = function(config){
35709 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35712 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35715 * @cfg {Boolean} isFitWidth - resize the width..
35717 isFitWidth : false, // options..
35719 * @cfg {Boolean} isOriginLeft = left align?
35721 isOriginLeft : true,
35723 * @cfg {Boolean} isOriginTop = top align?
35725 isOriginTop : false,
35727 * @cfg {Boolean} isLayoutInstant = no animation?
35729 isLayoutInstant : false, // needed?
35731 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35733 isResizingContainer : true,
35735 * @cfg {Number} columnWidth width of the columns
35741 * @cfg {Number} maxCols maximum number of columns
35746 * @cfg {Number} padHeight padding below box..
35752 * @cfg {Boolean} isAutoInitial defalut true
35755 isAutoInitial : true,
35761 initialColumnWidth : 0,
35762 currentSize : null,
35764 colYs : null, // array.
35771 bricks: null, //CompositeElement
35772 cols : 0, // array?
35773 // element : null, // wrapped now this.el
35774 _isLayoutInited : null,
35777 getAutoCreate : function(){
35781 cls: 'blog-masonary-wrapper ' + this.cls,
35783 cls : 'mas-boxes masonary'
35790 getChildContainer: function( )
35792 if (this.boxesEl) {
35793 return this.boxesEl;
35796 this.boxesEl = this.el.select('.mas-boxes').first();
35798 return this.boxesEl;
35802 initEvents : function()
35806 if(this.isAutoInitial){
35807 Roo.log('hook children rendered');
35808 this.on('childrenrendered', function() {
35809 Roo.log('children rendered');
35816 initial : function()
35818 this.reloadItems();
35820 this.currentSize = this.el.getBox(true);
35822 /// was window resize... - let's see if this works..
35823 Roo.EventManager.onWindowResize(this.resize, this);
35825 if(!this.isAutoInitial){
35830 this.layout.defer(500,this);
35833 reloadItems: function()
35835 this.bricks = this.el.select('.masonry-brick', true);
35837 this.bricks.each(function(b) {
35838 //Roo.log(b.getSize());
35839 if (!b.attr('originalwidth')) {
35840 b.attr('originalwidth', b.getSize().width);
35845 Roo.log(this.bricks.elements.length);
35848 resize : function()
35851 var cs = this.el.getBox(true);
35853 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35854 Roo.log("no change in with or X");
35857 this.currentSize = cs;
35861 layout : function()
35864 this._resetLayout();
35865 //this._manageStamps();
35867 // don't animate first layout
35868 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35869 this.layoutItems( isInstant );
35871 // flag for initalized
35872 this._isLayoutInited = true;
35875 layoutItems : function( isInstant )
35877 //var items = this._getItemsForLayout( this.items );
35878 // original code supports filtering layout items.. we just ignore it..
35880 this._layoutItems( this.bricks , isInstant );
35882 this._postLayout();
35884 _layoutItems : function ( items , isInstant)
35886 //this.fireEvent( 'layout', this, items );
35889 if ( !items || !items.elements.length ) {
35890 // no items, emit event with empty array
35895 items.each(function(item) {
35896 Roo.log("layout item");
35898 // get x/y object from method
35899 var position = this._getItemLayoutPosition( item );
35901 position.item = item;
35902 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35903 queue.push( position );
35906 this._processLayoutQueue( queue );
35908 /** Sets position of item in DOM
35909 * @param {Element} item
35910 * @param {Number} x - horizontal position
35911 * @param {Number} y - vertical position
35912 * @param {Boolean} isInstant - disables transitions
35914 _processLayoutQueue : function( queue )
35916 for ( var i=0, len = queue.length; i < len; i++ ) {
35917 var obj = queue[i];
35918 obj.item.position('absolute');
35919 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35925 * Any logic you want to do after each layout,
35926 * i.e. size the container
35928 _postLayout : function()
35930 this.resizeContainer();
35933 resizeContainer : function()
35935 if ( !this.isResizingContainer ) {
35938 var size = this._getContainerSize();
35940 this.el.setSize(size.width,size.height);
35941 this.boxesEl.setSize(size.width,size.height);
35947 _resetLayout : function()
35949 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35950 this.colWidth = this.el.getWidth();
35951 //this.gutter = this.el.getWidth();
35953 this.measureColumns();
35959 this.colYs.push( 0 );
35965 measureColumns : function()
35967 this.getContainerWidth();
35968 // if columnWidth is 0, default to outerWidth of first item
35969 if ( !this.columnWidth ) {
35970 var firstItem = this.bricks.first();
35971 Roo.log(firstItem);
35972 this.columnWidth = this.containerWidth;
35973 if (firstItem && firstItem.attr('originalwidth') ) {
35974 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35976 // columnWidth fall back to item of first element
35977 Roo.log("set column width?");
35978 this.initialColumnWidth = this.columnWidth ;
35980 // if first elem has no width, default to size of container
35985 if (this.initialColumnWidth) {
35986 this.columnWidth = this.initialColumnWidth;
35991 // column width is fixed at the top - however if container width get's smaller we should
35994 // this bit calcs how man columns..
35996 var columnWidth = this.columnWidth += this.gutter;
35998 // calculate columns
35999 var containerWidth = this.containerWidth + this.gutter;
36001 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36002 // fix rounding errors, typically with gutters
36003 var excess = columnWidth - containerWidth % columnWidth;
36006 // if overshoot is less than a pixel, round up, otherwise floor it
36007 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36008 cols = Math[ mathMethod ]( cols );
36009 this.cols = Math.max( cols, 1 );
36010 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36012 // padding positioning..
36013 var totalColWidth = this.cols * this.columnWidth;
36014 var padavail = this.containerWidth - totalColWidth;
36015 // so for 2 columns - we need 3 'pads'
36017 var padNeeded = (1+this.cols) * this.padWidth;
36019 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36021 this.columnWidth += padExtra
36022 //this.padWidth = Math.floor(padavail / ( this.cols));
36024 // adjust colum width so that padding is fixed??
36026 // we have 3 columns ... total = width * 3
36027 // we have X left over... that should be used by
36029 //if (this.expandC) {
36037 getContainerWidth : function()
36039 /* // container is parent if fit width
36040 var container = this.isFitWidth ? this.element.parentNode : this.element;
36041 // check that this.size and size are there
36042 // IE8 triggers resize on body size change, so they might not be
36044 var size = getSize( container ); //FIXME
36045 this.containerWidth = size && size.innerWidth; //FIXME
36048 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36052 _getItemLayoutPosition : function( item ) // what is item?
36054 // we resize the item to our columnWidth..
36056 item.setWidth(this.columnWidth);
36057 item.autoBoxAdjust = false;
36059 var sz = item.getSize();
36061 // how many columns does this brick span
36062 var remainder = this.containerWidth % this.columnWidth;
36064 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36065 // round if off by 1 pixel, otherwise use ceil
36066 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36067 colSpan = Math.min( colSpan, this.cols );
36069 // normally this should be '1' as we dont' currently allow multi width columns..
36071 var colGroup = this._getColGroup( colSpan );
36072 // get the minimum Y value from the columns
36073 var minimumY = Math.min.apply( Math, colGroup );
36074 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36076 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36078 // position the brick
36080 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36081 y: this.currentSize.y + minimumY + this.padHeight
36085 // apply setHeight to necessary columns
36086 var setHeight = minimumY + sz.height + this.padHeight;
36087 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36089 var setSpan = this.cols + 1 - colGroup.length;
36090 for ( var i = 0; i < setSpan; i++ ) {
36091 this.colYs[ shortColIndex + i ] = setHeight ;
36098 * @param {Number} colSpan - number of columns the element spans
36099 * @returns {Array} colGroup
36101 _getColGroup : function( colSpan )
36103 if ( colSpan < 2 ) {
36104 // if brick spans only one column, use all the column Ys
36109 // how many different places could this brick fit horizontally
36110 var groupCount = this.cols + 1 - colSpan;
36111 // for each group potential horizontal position
36112 for ( var i = 0; i < groupCount; i++ ) {
36113 // make an array of colY values for that one group
36114 var groupColYs = this.colYs.slice( i, i + colSpan );
36115 // and get the max value of the array
36116 colGroup[i] = Math.max.apply( Math, groupColYs );
36121 _manageStamp : function( stamp )
36123 var stampSize = stamp.getSize();
36124 var offset = stamp.getBox();
36125 // get the columns that this stamp affects
36126 var firstX = this.isOriginLeft ? offset.x : offset.right;
36127 var lastX = firstX + stampSize.width;
36128 var firstCol = Math.floor( firstX / this.columnWidth );
36129 firstCol = Math.max( 0, firstCol );
36131 var lastCol = Math.floor( lastX / this.columnWidth );
36132 // lastCol should not go over if multiple of columnWidth #425
36133 lastCol -= lastX % this.columnWidth ? 0 : 1;
36134 lastCol = Math.min( this.cols - 1, lastCol );
36136 // set colYs to bottom of the stamp
36137 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36140 for ( var i = firstCol; i <= lastCol; i++ ) {
36141 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36146 _getContainerSize : function()
36148 this.maxY = Math.max.apply( Math, this.colYs );
36153 if ( this.isFitWidth ) {
36154 size.width = this._getContainerFitWidth();
36160 _getContainerFitWidth : function()
36162 var unusedCols = 0;
36163 // count unused columns
36166 if ( this.colYs[i] !== 0 ) {
36171 // fit container to columns that have been used
36172 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36175 needsResizeLayout : function()
36177 var previousWidth = this.containerWidth;
36178 this.getContainerWidth();
36179 return previousWidth !== this.containerWidth;
36194 * @class Roo.bootstrap.MasonryBrick
36195 * @extends Roo.bootstrap.Component
36196 * Bootstrap MasonryBrick class
36199 * Create a new MasonryBrick
36200 * @param {Object} config The config object
36203 Roo.bootstrap.MasonryBrick = function(config){
36205 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36207 Roo.bootstrap.MasonryBrick.register(this);
36213 * When a MasonryBrick is clcik
36214 * @param {Roo.bootstrap.MasonryBrick} this
36215 * @param {Roo.EventObject} e
36221 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36224 * @cfg {String} title
36228 * @cfg {String} html
36232 * @cfg {String} bgimage
36236 * @cfg {String} videourl
36240 * @cfg {String} cls
36244 * @cfg {String} href
36248 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36253 * @cfg {String} placetitle (center|bottom)
36258 * @cfg {Boolean} isFitContainer defalut true
36260 isFitContainer : true,
36263 * @cfg {Boolean} preventDefault defalut false
36265 preventDefault : false,
36268 * @cfg {Boolean} inverse defalut false
36270 maskInverse : false,
36272 getAutoCreate : function()
36274 if(!this.isFitContainer){
36275 return this.getSplitAutoCreate();
36278 var cls = 'masonry-brick masonry-brick-full';
36280 if(this.href.length){
36281 cls += ' masonry-brick-link';
36284 if(this.bgimage.length){
36285 cls += ' masonry-brick-image';
36288 if(this.maskInverse){
36289 cls += ' mask-inverse';
36292 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36293 cls += ' enable-mask';
36297 cls += ' masonry-' + this.size + '-brick';
36300 if(this.placetitle.length){
36302 switch (this.placetitle) {
36304 cls += ' masonry-center-title';
36307 cls += ' masonry-bottom-title';
36314 if(!this.html.length && !this.bgimage.length){
36315 cls += ' masonry-center-title';
36318 if(!this.html.length && this.bgimage.length){
36319 cls += ' masonry-bottom-title';
36324 cls += ' ' + this.cls;
36328 tag: (this.href.length) ? 'a' : 'div',
36333 cls: 'masonry-brick-mask'
36337 cls: 'masonry-brick-paragraph',
36343 if(this.href.length){
36344 cfg.href = this.href;
36347 var cn = cfg.cn[1].cn;
36349 if(this.title.length){
36352 cls: 'masonry-brick-title',
36357 if(this.html.length){
36360 cls: 'masonry-brick-text',
36365 if (!this.title.length && !this.html.length) {
36366 cfg.cn[1].cls += ' hide';
36369 if(this.bgimage.length){
36372 cls: 'masonry-brick-image-view',
36377 if(this.videourl.length){
36378 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36379 // youtube support only?
36382 cls: 'masonry-brick-image-view',
36385 allowfullscreen : true
36393 getSplitAutoCreate : function()
36395 var cls = 'masonry-brick masonry-brick-split';
36397 if(this.href.length){
36398 cls += ' masonry-brick-link';
36401 if(this.bgimage.length){
36402 cls += ' masonry-brick-image';
36406 cls += ' masonry-' + this.size + '-brick';
36409 switch (this.placetitle) {
36411 cls += ' masonry-center-title';
36414 cls += ' masonry-bottom-title';
36417 if(!this.bgimage.length){
36418 cls += ' masonry-center-title';
36421 if(this.bgimage.length){
36422 cls += ' masonry-bottom-title';
36428 cls += ' ' + this.cls;
36432 tag: (this.href.length) ? 'a' : 'div',
36437 cls: 'masonry-brick-split-head',
36441 cls: 'masonry-brick-paragraph',
36448 cls: 'masonry-brick-split-body',
36454 if(this.href.length){
36455 cfg.href = this.href;
36458 if(this.title.length){
36459 cfg.cn[0].cn[0].cn.push({
36461 cls: 'masonry-brick-title',
36466 if(this.html.length){
36467 cfg.cn[1].cn.push({
36469 cls: 'masonry-brick-text',
36474 if(this.bgimage.length){
36475 cfg.cn[0].cn.push({
36477 cls: 'masonry-brick-image-view',
36482 if(this.videourl.length){
36483 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36484 // youtube support only?
36485 cfg.cn[0].cn.cn.push({
36487 cls: 'masonry-brick-image-view',
36490 allowfullscreen : true
36497 initEvents: function()
36499 switch (this.size) {
36532 this.el.on('touchstart', this.onTouchStart, this);
36533 this.el.on('touchmove', this.onTouchMove, this);
36534 this.el.on('touchend', this.onTouchEnd, this);
36535 this.el.on('contextmenu', this.onContextMenu, this);
36537 this.el.on('mouseenter' ,this.enter, this);
36538 this.el.on('mouseleave', this.leave, this);
36539 this.el.on('click', this.onClick, this);
36542 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36543 this.parent().bricks.push(this);
36548 onClick: function(e, el)
36550 var time = this.endTimer - this.startTimer;
36551 // Roo.log(e.preventDefault());
36554 e.preventDefault();
36559 if(!this.preventDefault){
36563 e.preventDefault();
36565 if (this.activeClass != '') {
36566 this.selectBrick();
36569 this.fireEvent('click', this, e);
36572 enter: function(e, el)
36574 e.preventDefault();
36576 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36580 if(this.bgimage.length && this.html.length){
36581 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36585 leave: function(e, el)
36587 e.preventDefault();
36589 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36593 if(this.bgimage.length && this.html.length){
36594 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36598 onTouchStart: function(e, el)
36600 // e.preventDefault();
36602 this.touchmoved = false;
36604 if(!this.isFitContainer){
36608 if(!this.bgimage.length || !this.html.length){
36612 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36614 this.timer = new Date().getTime();
36618 onTouchMove: function(e, el)
36620 this.touchmoved = true;
36623 onContextMenu : function(e,el)
36625 e.preventDefault();
36626 e.stopPropagation();
36630 onTouchEnd: function(e, el)
36632 // e.preventDefault();
36634 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36641 if(!this.bgimage.length || !this.html.length){
36643 if(this.href.length){
36644 window.location.href = this.href;
36650 if(!this.isFitContainer){
36654 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36656 window.location.href = this.href;
36659 //selection on single brick only
36660 selectBrick : function() {
36662 if (!this.parentId) {
36666 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36667 var index = m.selectedBrick.indexOf(this.id);
36670 m.selectedBrick.splice(index,1);
36671 this.el.removeClass(this.activeClass);
36675 for(var i = 0; i < m.selectedBrick.length; i++) {
36676 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36677 b.el.removeClass(b.activeClass);
36680 m.selectedBrick = [];
36682 m.selectedBrick.push(this.id);
36683 this.el.addClass(this.activeClass);
36687 isSelected : function(){
36688 return this.el.hasClass(this.activeClass);
36693 Roo.apply(Roo.bootstrap.MasonryBrick, {
36696 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36698 * register a Masonry Brick
36699 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36702 register : function(brick)
36704 //this.groups[brick.id] = brick;
36705 this.groups.add(brick.id, brick);
36708 * fetch a masonry brick based on the masonry brick ID
36709 * @param {string} the masonry brick to add
36710 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36713 get: function(brick_id)
36715 // if (typeof(this.groups[brick_id]) == 'undefined') {
36718 // return this.groups[brick_id] ;
36720 if(this.groups.key(brick_id)) {
36721 return this.groups.key(brick_id);
36739 * @class Roo.bootstrap.Brick
36740 * @extends Roo.bootstrap.Component
36741 * Bootstrap Brick class
36744 * Create a new Brick
36745 * @param {Object} config The config object
36748 Roo.bootstrap.Brick = function(config){
36749 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36755 * When a Brick is click
36756 * @param {Roo.bootstrap.Brick} this
36757 * @param {Roo.EventObject} e
36763 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36766 * @cfg {String} title
36770 * @cfg {String} html
36774 * @cfg {String} bgimage
36778 * @cfg {String} cls
36782 * @cfg {String} href
36786 * @cfg {String} video
36790 * @cfg {Boolean} square
36794 getAutoCreate : function()
36796 var cls = 'roo-brick';
36798 if(this.href.length){
36799 cls += ' roo-brick-link';
36802 if(this.bgimage.length){
36803 cls += ' roo-brick-image';
36806 if(!this.html.length && !this.bgimage.length){
36807 cls += ' roo-brick-center-title';
36810 if(!this.html.length && this.bgimage.length){
36811 cls += ' roo-brick-bottom-title';
36815 cls += ' ' + this.cls;
36819 tag: (this.href.length) ? 'a' : 'div',
36824 cls: 'roo-brick-paragraph',
36830 if(this.href.length){
36831 cfg.href = this.href;
36834 var cn = cfg.cn[0].cn;
36836 if(this.title.length){
36839 cls: 'roo-brick-title',
36844 if(this.html.length){
36847 cls: 'roo-brick-text',
36854 if(this.bgimage.length){
36857 cls: 'roo-brick-image-view',
36865 initEvents: function()
36867 if(this.title.length || this.html.length){
36868 this.el.on('mouseenter' ,this.enter, this);
36869 this.el.on('mouseleave', this.leave, this);
36872 Roo.EventManager.onWindowResize(this.resize, this);
36874 if(this.bgimage.length){
36875 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36876 this.imageEl.on('load', this.onImageLoad, this);
36883 onImageLoad : function()
36888 resize : function()
36890 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36892 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36894 if(this.bgimage.length){
36895 var image = this.el.select('.roo-brick-image-view', true).first();
36897 image.setWidth(paragraph.getWidth());
36900 image.setHeight(paragraph.getWidth());
36903 this.el.setHeight(image.getHeight());
36904 paragraph.setHeight(image.getHeight());
36910 enter: function(e, el)
36912 e.preventDefault();
36914 if(this.bgimage.length){
36915 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36916 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36920 leave: function(e, el)
36922 e.preventDefault();
36924 if(this.bgimage.length){
36925 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36926 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36941 * @class Roo.bootstrap.NumberField
36942 * @extends Roo.bootstrap.Input
36943 * Bootstrap NumberField class
36949 * Create a new NumberField
36950 * @param {Object} config The config object
36953 Roo.bootstrap.NumberField = function(config){
36954 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36957 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36960 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36962 allowDecimals : true,
36964 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36966 decimalSeparator : ".",
36968 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36970 decimalPrecision : 2,
36972 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36974 allowNegative : true,
36977 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36981 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36983 minValue : Number.NEGATIVE_INFINITY,
36985 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36987 maxValue : Number.MAX_VALUE,
36989 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36991 minText : "The minimum value for this field is {0}",
36993 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36995 maxText : "The maximum value for this field is {0}",
36997 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36998 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37000 nanText : "{0} is not a valid number",
37002 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37004 thousandsDelimiter : false,
37006 * @cfg {String} valueAlign alignment of value
37008 valueAlign : "left",
37010 getAutoCreate : function()
37012 var hiddenInput = {
37016 cls: 'hidden-number-input'
37020 hiddenInput.name = this.name;
37025 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37027 this.name = hiddenInput.name;
37029 if(cfg.cn.length > 0) {
37030 cfg.cn.push(hiddenInput);
37037 initEvents : function()
37039 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37041 var allowed = "0123456789";
37043 if(this.allowDecimals){
37044 allowed += this.decimalSeparator;
37047 if(this.allowNegative){
37051 if(this.thousandsDelimiter) {
37055 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37057 var keyPress = function(e){
37059 var k = e.getKey();
37061 var c = e.getCharCode();
37064 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37065 allowed.indexOf(String.fromCharCode(c)) === -1
37071 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37075 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37080 this.el.on("keypress", keyPress, this);
37083 validateValue : function(value)
37086 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37090 var num = this.parseValue(value);
37093 this.markInvalid(String.format(this.nanText, value));
37097 if(num < this.minValue){
37098 this.markInvalid(String.format(this.minText, this.minValue));
37102 if(num > this.maxValue){
37103 this.markInvalid(String.format(this.maxText, this.maxValue));
37110 getValue : function()
37112 var v = this.hiddenEl().getValue();
37114 return this.fixPrecision(this.parseValue(v));
37117 parseValue : function(value)
37119 if(this.thousandsDelimiter) {
37121 r = new RegExp(",", "g");
37122 value = value.replace(r, "");
37125 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37126 return isNaN(value) ? '' : value;
37129 fixPrecision : function(value)
37131 if(this.thousandsDelimiter) {
37133 r = new RegExp(",", "g");
37134 value = value.replace(r, "");
37137 var nan = isNaN(value);
37139 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37140 return nan ? '' : value;
37142 return parseFloat(value).toFixed(this.decimalPrecision);
37145 setValue : function(v)
37147 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37153 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37155 this.inputEl().dom.value = (v == '') ? '' :
37156 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37158 if(!this.allowZero && v === '0') {
37159 this.hiddenEl().dom.value = '';
37160 this.inputEl().dom.value = '';
37167 decimalPrecisionFcn : function(v)
37169 return Math.floor(v);
37172 beforeBlur : function()
37174 var v = this.parseValue(this.getRawValue());
37176 if(v || v === 0 || v === ''){
37181 hiddenEl : function()
37183 return this.el.select('input.hidden-number-input',true).first();
37195 * @class Roo.bootstrap.DocumentSlider
37196 * @extends Roo.bootstrap.Component
37197 * Bootstrap DocumentSlider class
37200 * Create a new DocumentViewer
37201 * @param {Object} config The config object
37204 Roo.bootstrap.DocumentSlider = function(config){
37205 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37212 * Fire after initEvent
37213 * @param {Roo.bootstrap.DocumentSlider} this
37218 * Fire after update
37219 * @param {Roo.bootstrap.DocumentSlider} this
37225 * @param {Roo.bootstrap.DocumentSlider} this
37231 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37237 getAutoCreate : function()
37241 cls : 'roo-document-slider',
37245 cls : 'roo-document-slider-header',
37249 cls : 'roo-document-slider-header-title'
37255 cls : 'roo-document-slider-body',
37259 cls : 'roo-document-slider-prev',
37263 cls : 'fa fa-chevron-left'
37269 cls : 'roo-document-slider-thumb',
37273 cls : 'roo-document-slider-image'
37279 cls : 'roo-document-slider-next',
37283 cls : 'fa fa-chevron-right'
37295 initEvents : function()
37297 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37298 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37300 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37301 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37303 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37304 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37306 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37307 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37309 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37310 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37312 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37313 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37315 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37316 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37318 this.thumbEl.on('click', this.onClick, this);
37320 this.prevIndicator.on('click', this.prev, this);
37322 this.nextIndicator.on('click', this.next, this);
37326 initial : function()
37328 if(this.files.length){
37329 this.indicator = 1;
37333 this.fireEvent('initial', this);
37336 update : function()
37338 this.imageEl.attr('src', this.files[this.indicator - 1]);
37340 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37342 this.prevIndicator.show();
37344 if(this.indicator == 1){
37345 this.prevIndicator.hide();
37348 this.nextIndicator.show();
37350 if(this.indicator == this.files.length){
37351 this.nextIndicator.hide();
37354 this.thumbEl.scrollTo('top');
37356 this.fireEvent('update', this);
37359 onClick : function(e)
37361 e.preventDefault();
37363 this.fireEvent('click', this);
37368 e.preventDefault();
37370 this.indicator = Math.max(1, this.indicator - 1);
37377 e.preventDefault();
37379 this.indicator = Math.min(this.files.length, this.indicator + 1);
37393 * @class Roo.bootstrap.RadioSet
37394 * @extends Roo.bootstrap.Input
37395 * Bootstrap RadioSet class
37396 * @cfg {String} indicatorpos (left|right) default left
37397 * @cfg {Boolean} inline (true|false) inline the element (default true)
37398 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37400 * Create a new RadioSet
37401 * @param {Object} config The config object
37404 Roo.bootstrap.RadioSet = function(config){
37406 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37410 Roo.bootstrap.RadioSet.register(this);
37415 * Fires when the element is checked or unchecked.
37416 * @param {Roo.bootstrap.RadioSet} this This radio
37417 * @param {Roo.bootstrap.Radio} item The checked item
37422 * Fires when the element is click.
37423 * @param {Roo.bootstrap.RadioSet} this This radio set
37424 * @param {Roo.bootstrap.Radio} item The checked item
37425 * @param {Roo.EventObject} e The event object
37432 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37440 indicatorpos : 'left',
37442 getAutoCreate : function()
37446 cls : 'roo-radio-set-label',
37450 html : this.fieldLabel
37454 if (Roo.bootstrap.version == 3) {
37457 if(this.indicatorpos == 'left'){
37460 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37461 tooltip : 'This field is required'
37466 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37467 tooltip : 'This field is required'
37473 cls : 'roo-radio-set-items'
37476 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37478 if (align === 'left' && this.fieldLabel.length) {
37481 cls : "roo-radio-set-right",
37487 if(this.labelWidth > 12){
37488 label.style = "width: " + this.labelWidth + 'px';
37491 if(this.labelWidth < 13 && this.labelmd == 0){
37492 this.labelmd = this.labelWidth;
37495 if(this.labellg > 0){
37496 label.cls += ' col-lg-' + this.labellg;
37497 items.cls += ' col-lg-' + (12 - this.labellg);
37500 if(this.labelmd > 0){
37501 label.cls += ' col-md-' + this.labelmd;
37502 items.cls += ' col-md-' + (12 - this.labelmd);
37505 if(this.labelsm > 0){
37506 label.cls += ' col-sm-' + this.labelsm;
37507 items.cls += ' col-sm-' + (12 - this.labelsm);
37510 if(this.labelxs > 0){
37511 label.cls += ' col-xs-' + this.labelxs;
37512 items.cls += ' col-xs-' + (12 - this.labelxs);
37518 cls : 'roo-radio-set',
37522 cls : 'roo-radio-set-input',
37525 value : this.value ? this.value : ''
37532 if(this.weight.length){
37533 cfg.cls += ' roo-radio-' + this.weight;
37537 cfg.cls += ' roo-radio-set-inline';
37541 ['xs','sm','md','lg'].map(function(size){
37542 if (settings[size]) {
37543 cfg.cls += ' col-' + size + '-' + settings[size];
37551 initEvents : function()
37553 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37554 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37556 if(!this.fieldLabel.length){
37557 this.labelEl.hide();
37560 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37561 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37563 this.indicator = this.indicatorEl();
37565 if(this.indicator){
37566 this.indicator.addClass('invisible');
37569 this.originalValue = this.getValue();
37573 inputEl: function ()
37575 return this.el.select('.roo-radio-set-input', true).first();
37578 getChildContainer : function()
37580 return this.itemsEl;
37583 register : function(item)
37585 this.radioes.push(item);
37589 validate : function()
37591 if(this.getVisibilityEl().hasClass('hidden')){
37597 Roo.each(this.radioes, function(i){
37606 if(this.allowBlank) {
37610 if(this.disabled || valid){
37615 this.markInvalid();
37620 markValid : function()
37622 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37623 this.indicatorEl().removeClass('visible');
37624 this.indicatorEl().addClass('invisible');
37628 if (Roo.bootstrap.version == 3) {
37629 this.el.removeClass([this.invalidClass, this.validClass]);
37630 this.el.addClass(this.validClass);
37632 this.el.removeClass(['is-invalid','is-valid']);
37633 this.el.addClass(['is-valid']);
37635 this.fireEvent('valid', this);
37638 markInvalid : function(msg)
37640 if(this.allowBlank || this.disabled){
37644 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37645 this.indicatorEl().removeClass('invisible');
37646 this.indicatorEl().addClass('visible');
37648 if (Roo.bootstrap.version == 3) {
37649 this.el.removeClass([this.invalidClass, this.validClass]);
37650 this.el.addClass(this.invalidClass);
37652 this.el.removeClass(['is-invalid','is-valid']);
37653 this.el.addClass(['is-invalid']);
37656 this.fireEvent('invalid', this, msg);
37660 setValue : function(v, suppressEvent)
37662 if(this.value === v){
37669 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37672 Roo.each(this.radioes, function(i){
37674 i.el.removeClass('checked');
37677 Roo.each(this.radioes, function(i){
37679 if(i.value === v || i.value.toString() === v.toString()){
37681 i.el.addClass('checked');
37683 if(suppressEvent !== true){
37684 this.fireEvent('check', this, i);
37695 clearInvalid : function(){
37697 if(!this.el || this.preventMark){
37701 this.el.removeClass([this.invalidClass]);
37703 this.fireEvent('valid', this);
37708 Roo.apply(Roo.bootstrap.RadioSet, {
37712 register : function(set)
37714 this.groups[set.name] = set;
37717 get: function(name)
37719 if (typeof(this.groups[name]) == 'undefined') {
37723 return this.groups[name] ;
37729 * Ext JS Library 1.1.1
37730 * Copyright(c) 2006-2007, Ext JS, LLC.
37732 * Originally Released Under LGPL - original licence link has changed is not relivant.
37735 * <script type="text/javascript">
37740 * @class Roo.bootstrap.SplitBar
37741 * @extends Roo.util.Observable
37742 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37746 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37747 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37748 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37749 split.minSize = 100;
37750 split.maxSize = 600;
37751 split.animate = true;
37752 split.on('moved', splitterMoved);
37755 * Create a new SplitBar
37756 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37757 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37758 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37759 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37760 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37761 position of the SplitBar).
37763 Roo.bootstrap.SplitBar = function(cfg){
37768 // dragElement : elm
37769 // resizingElement: el,
37771 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37772 // placement : Roo.bootstrap.SplitBar.LEFT ,
37773 // existingProxy ???
37776 this.el = Roo.get(cfg.dragElement, true);
37777 this.el.dom.unselectable = "on";
37779 this.resizingEl = Roo.get(cfg.resizingElement, true);
37783 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37784 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37787 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37790 * The minimum size of the resizing element. (Defaults to 0)
37796 * The maximum size of the resizing element. (Defaults to 2000)
37799 this.maxSize = 2000;
37802 * Whether to animate the transition to the new size
37805 this.animate = false;
37808 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37811 this.useShim = false;
37816 if(!cfg.existingProxy){
37818 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37820 this.proxy = Roo.get(cfg.existingProxy).dom;
37823 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37826 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37829 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37832 this.dragSpecs = {};
37835 * @private The adapter to use to positon and resize elements
37837 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37838 this.adapter.init(this);
37840 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37842 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37843 this.el.addClass("roo-splitbar-h");
37846 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37847 this.el.addClass("roo-splitbar-v");
37853 * Fires when the splitter is moved (alias for {@link #event-moved})
37854 * @param {Roo.bootstrap.SplitBar} this
37855 * @param {Number} newSize the new width or height
37860 * Fires when the splitter is moved
37861 * @param {Roo.bootstrap.SplitBar} this
37862 * @param {Number} newSize the new width or height
37866 * @event beforeresize
37867 * Fires before the splitter is dragged
37868 * @param {Roo.bootstrap.SplitBar} this
37870 "beforeresize" : true,
37872 "beforeapply" : true
37875 Roo.util.Observable.call(this);
37878 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37879 onStartProxyDrag : function(x, y){
37880 this.fireEvent("beforeresize", this);
37882 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37884 o.enableDisplayMode("block");
37885 // all splitbars share the same overlay
37886 Roo.bootstrap.SplitBar.prototype.overlay = o;
37888 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37889 this.overlay.show();
37890 Roo.get(this.proxy).setDisplayed("block");
37891 var size = this.adapter.getElementSize(this);
37892 this.activeMinSize = this.getMinimumSize();;
37893 this.activeMaxSize = this.getMaximumSize();;
37894 var c1 = size - this.activeMinSize;
37895 var c2 = Math.max(this.activeMaxSize - size, 0);
37896 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37897 this.dd.resetConstraints();
37898 this.dd.setXConstraint(
37899 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37900 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37902 this.dd.setYConstraint(0, 0);
37904 this.dd.resetConstraints();
37905 this.dd.setXConstraint(0, 0);
37906 this.dd.setYConstraint(
37907 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37908 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37911 this.dragSpecs.startSize = size;
37912 this.dragSpecs.startPoint = [x, y];
37913 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37917 * @private Called after the drag operation by the DDProxy
37919 onEndProxyDrag : function(e){
37920 Roo.get(this.proxy).setDisplayed(false);
37921 var endPoint = Roo.lib.Event.getXY(e);
37923 this.overlay.hide();
37926 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37927 newSize = this.dragSpecs.startSize +
37928 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37929 endPoint[0] - this.dragSpecs.startPoint[0] :
37930 this.dragSpecs.startPoint[0] - endPoint[0]
37933 newSize = this.dragSpecs.startSize +
37934 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37935 endPoint[1] - this.dragSpecs.startPoint[1] :
37936 this.dragSpecs.startPoint[1] - endPoint[1]
37939 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37940 if(newSize != this.dragSpecs.startSize){
37941 if(this.fireEvent('beforeapply', this, newSize) !== false){
37942 this.adapter.setElementSize(this, newSize);
37943 this.fireEvent("moved", this, newSize);
37944 this.fireEvent("resize", this, newSize);
37950 * Get the adapter this SplitBar uses
37951 * @return The adapter object
37953 getAdapter : function(){
37954 return this.adapter;
37958 * Set the adapter this SplitBar uses
37959 * @param {Object} adapter A SplitBar adapter object
37961 setAdapter : function(adapter){
37962 this.adapter = adapter;
37963 this.adapter.init(this);
37967 * Gets the minimum size for the resizing element
37968 * @return {Number} The minimum size
37970 getMinimumSize : function(){
37971 return this.minSize;
37975 * Sets the minimum size for the resizing element
37976 * @param {Number} minSize The minimum size
37978 setMinimumSize : function(minSize){
37979 this.minSize = minSize;
37983 * Gets the maximum size for the resizing element
37984 * @return {Number} The maximum size
37986 getMaximumSize : function(){
37987 return this.maxSize;
37991 * Sets the maximum size for the resizing element
37992 * @param {Number} maxSize The maximum size
37994 setMaximumSize : function(maxSize){
37995 this.maxSize = maxSize;
37999 * Sets the initialize size for the resizing element
38000 * @param {Number} size The initial size
38002 setCurrentSize : function(size){
38003 var oldAnimate = this.animate;
38004 this.animate = false;
38005 this.adapter.setElementSize(this, size);
38006 this.animate = oldAnimate;
38010 * Destroy this splitbar.
38011 * @param {Boolean} removeEl True to remove the element
38013 destroy : function(removeEl){
38015 this.shim.remove();
38018 this.proxy.parentNode.removeChild(this.proxy);
38026 * @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.
38028 Roo.bootstrap.SplitBar.createProxy = function(dir){
38029 var proxy = new Roo.Element(document.createElement("div"));
38030 proxy.unselectable();
38031 var cls = 'roo-splitbar-proxy';
38032 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38033 document.body.appendChild(proxy.dom);
38038 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38039 * Default Adapter. It assumes the splitter and resizing element are not positioned
38040 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38042 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38045 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38046 // do nothing for now
38047 init : function(s){
38051 * Called before drag operations to get the current size of the resizing element.
38052 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38054 getElementSize : function(s){
38055 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38056 return s.resizingEl.getWidth();
38058 return s.resizingEl.getHeight();
38063 * Called after drag operations to set the size of the resizing element.
38064 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38065 * @param {Number} newSize The new size to set
38066 * @param {Function} onComplete A function to be invoked when resizing is complete
38068 setElementSize : function(s, newSize, onComplete){
38069 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38071 s.resizingEl.setWidth(newSize);
38073 onComplete(s, newSize);
38076 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38081 s.resizingEl.setHeight(newSize);
38083 onComplete(s, newSize);
38086 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38093 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38094 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38095 * Adapter that moves the splitter element to align with the resized sizing element.
38096 * Used with an absolute positioned SplitBar.
38097 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38098 * document.body, make sure you assign an id to the body element.
38100 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38101 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38102 this.container = Roo.get(container);
38105 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38106 init : function(s){
38107 this.basic.init(s);
38110 getElementSize : function(s){
38111 return this.basic.getElementSize(s);
38114 setElementSize : function(s, newSize, onComplete){
38115 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38118 moveSplitter : function(s){
38119 var yes = Roo.bootstrap.SplitBar;
38120 switch(s.placement){
38122 s.el.setX(s.resizingEl.getRight());
38125 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38128 s.el.setY(s.resizingEl.getBottom());
38131 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38138 * Orientation constant - Create a vertical SplitBar
38142 Roo.bootstrap.SplitBar.VERTICAL = 1;
38145 * Orientation constant - Create a horizontal SplitBar
38149 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38152 * Placement constant - The resizing element is to the left of the splitter element
38156 Roo.bootstrap.SplitBar.LEFT = 1;
38159 * Placement constant - The resizing element is to the right of the splitter element
38163 Roo.bootstrap.SplitBar.RIGHT = 2;
38166 * Placement constant - The resizing element is positioned above the splitter element
38170 Roo.bootstrap.SplitBar.TOP = 3;
38173 * Placement constant - The resizing element is positioned under splitter element
38177 Roo.bootstrap.SplitBar.BOTTOM = 4;
38178 Roo.namespace("Roo.bootstrap.layout");/*
38180 * Ext JS Library 1.1.1
38181 * Copyright(c) 2006-2007, Ext JS, LLC.
38183 * Originally Released Under LGPL - original licence link has changed is not relivant.
38186 * <script type="text/javascript">
38190 * @class Roo.bootstrap.layout.Manager
38191 * @extends Roo.bootstrap.Component
38192 * Base class for layout managers.
38194 Roo.bootstrap.layout.Manager = function(config)
38196 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38202 /** false to disable window resize monitoring @type Boolean */
38203 this.monitorWindowResize = true;
38208 * Fires when a layout is performed.
38209 * @param {Roo.LayoutManager} this
38213 * @event regionresized
38214 * Fires when the user resizes a region.
38215 * @param {Roo.LayoutRegion} region The resized region
38216 * @param {Number} newSize The new size (width for east/west, height for north/south)
38218 "regionresized" : true,
38220 * @event regioncollapsed
38221 * Fires when a region is collapsed.
38222 * @param {Roo.LayoutRegion} region The collapsed region
38224 "regioncollapsed" : true,
38226 * @event regionexpanded
38227 * Fires when a region is expanded.
38228 * @param {Roo.LayoutRegion} region The expanded region
38230 "regionexpanded" : true
38232 this.updating = false;
38235 this.el = Roo.get(config.el);
38241 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38246 monitorWindowResize : true,
38252 onRender : function(ct, position)
38255 this.el = Roo.get(ct);
38258 //this.fireEvent('render',this);
38262 initEvents: function()
38266 // ie scrollbar fix
38267 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38268 document.body.scroll = "no";
38269 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38270 this.el.position('relative');
38272 this.id = this.el.id;
38273 this.el.addClass("roo-layout-container");
38274 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38275 if(this.el.dom != document.body ) {
38276 this.el.on('resize', this.layout,this);
38277 this.el.on('show', this.layout,this);
38283 * Returns true if this layout is currently being updated
38284 * @return {Boolean}
38286 isUpdating : function(){
38287 return this.updating;
38291 * Suspend the LayoutManager from doing auto-layouts while
38292 * making multiple add or remove calls
38294 beginUpdate : function(){
38295 this.updating = true;
38299 * Restore auto-layouts and optionally disable the manager from performing a layout
38300 * @param {Boolean} noLayout true to disable a layout update
38302 endUpdate : function(noLayout){
38303 this.updating = false;
38309 layout: function(){
38313 onRegionResized : function(region, newSize){
38314 this.fireEvent("regionresized", region, newSize);
38318 onRegionCollapsed : function(region){
38319 this.fireEvent("regioncollapsed", region);
38322 onRegionExpanded : function(region){
38323 this.fireEvent("regionexpanded", region);
38327 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38328 * performs box-model adjustments.
38329 * @return {Object} The size as an object {width: (the width), height: (the height)}
38331 getViewSize : function()
38334 if(this.el.dom != document.body){
38335 size = this.el.getSize();
38337 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38339 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38340 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38345 * Returns the Element this layout is bound to.
38346 * @return {Roo.Element}
38348 getEl : function(){
38353 * Returns the specified region.
38354 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38355 * @return {Roo.LayoutRegion}
38357 getRegion : function(target){
38358 return this.regions[target.toLowerCase()];
38361 onWindowResize : function(){
38362 if(this.monitorWindowResize){
38369 * Ext JS Library 1.1.1
38370 * Copyright(c) 2006-2007, Ext JS, LLC.
38372 * Originally Released Under LGPL - original licence link has changed is not relivant.
38375 * <script type="text/javascript">
38378 * @class Roo.bootstrap.layout.Border
38379 * @extends Roo.bootstrap.layout.Manager
38381 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38382 * please see: examples/bootstrap/nested.html<br><br>
38384 <b>The container the layout is rendered into can be either the body element or any other element.
38385 If it is not the body element, the container needs to either be an absolute positioned element,
38386 or you will need to add "position:relative" to the css of the container. You will also need to specify
38387 the container size if it is not the body element.</b>
38390 * Create a new Border
38391 * @param {Object} config Configuration options
38393 Roo.bootstrap.layout.Border = function(config){
38394 config = config || {};
38395 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38399 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38400 if(config[region]){
38401 config[region].region = region;
38402 this.addRegion(config[region]);
38408 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38410 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38412 parent : false, // this might point to a 'nest' or a ???
38415 * Creates and adds a new region if it doesn't already exist.
38416 * @param {String} target The target region key (north, south, east, west or center).
38417 * @param {Object} config The regions config object
38418 * @return {BorderLayoutRegion} The new region
38420 addRegion : function(config)
38422 if(!this.regions[config.region]){
38423 var r = this.factory(config);
38424 this.bindRegion(r);
38426 return this.regions[config.region];
38430 bindRegion : function(r){
38431 this.regions[r.config.region] = r;
38433 r.on("visibilitychange", this.layout, this);
38434 r.on("paneladded", this.layout, this);
38435 r.on("panelremoved", this.layout, this);
38436 r.on("invalidated", this.layout, this);
38437 r.on("resized", this.onRegionResized, this);
38438 r.on("collapsed", this.onRegionCollapsed, this);
38439 r.on("expanded", this.onRegionExpanded, this);
38443 * Performs a layout update.
38445 layout : function()
38447 if(this.updating) {
38451 // render all the rebions if they have not been done alreayd?
38452 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38453 if(this.regions[region] && !this.regions[region].bodyEl){
38454 this.regions[region].onRender(this.el)
38458 var size = this.getViewSize();
38459 var w = size.width;
38460 var h = size.height;
38465 //var x = 0, y = 0;
38467 var rs = this.regions;
38468 var north = rs["north"];
38469 var south = rs["south"];
38470 var west = rs["west"];
38471 var east = rs["east"];
38472 var center = rs["center"];
38473 //if(this.hideOnLayout){ // not supported anymore
38474 //c.el.setStyle("display", "none");
38476 if(north && north.isVisible()){
38477 var b = north.getBox();
38478 var m = north.getMargins();
38479 b.width = w - (m.left+m.right);
38482 centerY = b.height + b.y + m.bottom;
38483 centerH -= centerY;
38484 north.updateBox(this.safeBox(b));
38486 if(south && south.isVisible()){
38487 var b = south.getBox();
38488 var m = south.getMargins();
38489 b.width = w - (m.left+m.right);
38491 var totalHeight = (b.height + m.top + m.bottom);
38492 b.y = h - totalHeight + m.top;
38493 centerH -= totalHeight;
38494 south.updateBox(this.safeBox(b));
38496 if(west && west.isVisible()){
38497 var b = west.getBox();
38498 var m = west.getMargins();
38499 b.height = centerH - (m.top+m.bottom);
38501 b.y = centerY + m.top;
38502 var totalWidth = (b.width + m.left + m.right);
38503 centerX += totalWidth;
38504 centerW -= totalWidth;
38505 west.updateBox(this.safeBox(b));
38507 if(east && east.isVisible()){
38508 var b = east.getBox();
38509 var m = east.getMargins();
38510 b.height = centerH - (m.top+m.bottom);
38511 var totalWidth = (b.width + m.left + m.right);
38512 b.x = w - totalWidth + m.left;
38513 b.y = centerY + m.top;
38514 centerW -= totalWidth;
38515 east.updateBox(this.safeBox(b));
38518 var m = center.getMargins();
38520 x: centerX + m.left,
38521 y: centerY + m.top,
38522 width: centerW - (m.left+m.right),
38523 height: centerH - (m.top+m.bottom)
38525 //if(this.hideOnLayout){
38526 //center.el.setStyle("display", "block");
38528 center.updateBox(this.safeBox(centerBox));
38531 this.fireEvent("layout", this);
38535 safeBox : function(box){
38536 box.width = Math.max(0, box.width);
38537 box.height = Math.max(0, box.height);
38542 * Adds a ContentPanel (or subclass) to this layout.
38543 * @param {String} target The target region key (north, south, east, west or center).
38544 * @param {Roo.ContentPanel} panel The panel to add
38545 * @return {Roo.ContentPanel} The added panel
38547 add : function(target, panel){
38549 target = target.toLowerCase();
38550 return this.regions[target].add(panel);
38554 * Remove a ContentPanel (or subclass) to this layout.
38555 * @param {String} target The target region key (north, south, east, west or center).
38556 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38557 * @return {Roo.ContentPanel} The removed panel
38559 remove : function(target, panel){
38560 target = target.toLowerCase();
38561 return this.regions[target].remove(panel);
38565 * Searches all regions for a panel with the specified id
38566 * @param {String} panelId
38567 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38569 findPanel : function(panelId){
38570 var rs = this.regions;
38571 for(var target in rs){
38572 if(typeof rs[target] != "function"){
38573 var p = rs[target].getPanel(panelId);
38583 * Searches all regions for a panel with the specified id and activates (shows) it.
38584 * @param {String/ContentPanel} panelId The panels id or the panel itself
38585 * @return {Roo.ContentPanel} The shown panel or null
38587 showPanel : function(panelId) {
38588 var rs = this.regions;
38589 for(var target in rs){
38590 var r = rs[target];
38591 if(typeof r != "function"){
38592 if(r.hasPanel(panelId)){
38593 return r.showPanel(panelId);
38601 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38602 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38605 restoreState : function(provider){
38607 provider = Roo.state.Manager;
38609 var sm = new Roo.LayoutStateManager();
38610 sm.init(this, provider);
38616 * Adds a xtype elements to the layout.
38620 xtype : 'ContentPanel',
38627 xtype : 'NestedLayoutPanel',
38633 items : [ ... list of content panels or nested layout panels.. ]
38637 * @param {Object} cfg Xtype definition of item to add.
38639 addxtype : function(cfg)
38641 // basically accepts a pannel...
38642 // can accept a layout region..!?!?
38643 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38646 // theory? children can only be panels??
38648 //if (!cfg.xtype.match(/Panel$/)) {
38653 if (typeof(cfg.region) == 'undefined') {
38654 Roo.log("Failed to add Panel, region was not set");
38658 var region = cfg.region;
38664 xitems = cfg.items;
38669 if ( region == 'center') {
38670 Roo.log("Center: " + cfg.title);
38676 case 'Content': // ContentPanel (el, cfg)
38677 case 'Scroll': // ContentPanel (el, cfg)
38679 cfg.autoCreate = cfg.autoCreate || true;
38680 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38682 // var el = this.el.createChild();
38683 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38686 this.add(region, ret);
38690 case 'TreePanel': // our new panel!
38691 cfg.el = this.el.createChild();
38692 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38693 this.add(region, ret);
38698 // create a new Layout (which is a Border Layout...
38700 var clayout = cfg.layout;
38701 clayout.el = this.el.createChild();
38702 clayout.items = clayout.items || [];
38706 // replace this exitems with the clayout ones..
38707 xitems = clayout.items;
38709 // force background off if it's in center...
38710 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38711 cfg.background = false;
38713 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38716 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38717 //console.log('adding nested layout panel ' + cfg.toSource());
38718 this.add(region, ret);
38719 nb = {}; /// find first...
38724 // needs grid and region
38726 //var el = this.getRegion(region).el.createChild();
38728 *var el = this.el.createChild();
38729 // create the grid first...
38730 cfg.grid.container = el;
38731 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38734 if (region == 'center' && this.active ) {
38735 cfg.background = false;
38738 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38740 this.add(region, ret);
38742 if (cfg.background) {
38743 // render grid on panel activation (if panel background)
38744 ret.on('activate', function(gp) {
38745 if (!gp.grid.rendered) {
38746 // gp.grid.render(el);
38750 // cfg.grid.render(el);
38756 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38757 // it was the old xcomponent building that caused this before.
38758 // espeically if border is the top element in the tree.
38768 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38770 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38771 this.add(region, ret);
38775 throw "Can not add '" + cfg.xtype + "' to Border";
38781 this.beginUpdate();
38785 Roo.each(xitems, function(i) {
38786 region = nb && i.region ? i.region : false;
38788 var add = ret.addxtype(i);
38791 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38792 if (!i.background) {
38793 abn[region] = nb[region] ;
38800 // make the last non-background panel active..
38801 //if (nb) { Roo.log(abn); }
38804 for(var r in abn) {
38805 region = this.getRegion(r);
38807 // tried using nb[r], but it does not work..
38809 region.showPanel(abn[r]);
38820 factory : function(cfg)
38823 var validRegions = Roo.bootstrap.layout.Border.regions;
38825 var target = cfg.region;
38828 var r = Roo.bootstrap.layout;
38832 return new r.North(cfg);
38834 return new r.South(cfg);
38836 return new r.East(cfg);
38838 return new r.West(cfg);
38840 return new r.Center(cfg);
38842 throw 'Layout region "'+target+'" not supported.';
38849 * Ext JS Library 1.1.1
38850 * Copyright(c) 2006-2007, Ext JS, LLC.
38852 * Originally Released Under LGPL - original licence link has changed is not relivant.
38855 * <script type="text/javascript">
38859 * @class Roo.bootstrap.layout.Basic
38860 * @extends Roo.util.Observable
38861 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38862 * and does not have a titlebar, tabs or any other features. All it does is size and position
38863 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38864 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38865 * @cfg {string} region the region that it inhabits..
38866 * @cfg {bool} skipConfig skip config?
38870 Roo.bootstrap.layout.Basic = function(config){
38872 this.mgr = config.mgr;
38874 this.position = config.region;
38876 var skipConfig = config.skipConfig;
38880 * @scope Roo.BasicLayoutRegion
38884 * @event beforeremove
38885 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38886 * @param {Roo.LayoutRegion} this
38887 * @param {Roo.ContentPanel} panel The panel
38888 * @param {Object} e The cancel event object
38890 "beforeremove" : true,
38892 * @event invalidated
38893 * Fires when the layout for this region is changed.
38894 * @param {Roo.LayoutRegion} this
38896 "invalidated" : true,
38898 * @event visibilitychange
38899 * Fires when this region is shown or hidden
38900 * @param {Roo.LayoutRegion} this
38901 * @param {Boolean} visibility true or false
38903 "visibilitychange" : true,
38905 * @event paneladded
38906 * Fires when a panel is added.
38907 * @param {Roo.LayoutRegion} this
38908 * @param {Roo.ContentPanel} panel The panel
38910 "paneladded" : true,
38912 * @event panelremoved
38913 * Fires when a panel is removed.
38914 * @param {Roo.LayoutRegion} this
38915 * @param {Roo.ContentPanel} panel The panel
38917 "panelremoved" : true,
38919 * @event beforecollapse
38920 * Fires when this region before collapse.
38921 * @param {Roo.LayoutRegion} this
38923 "beforecollapse" : true,
38926 * Fires when this region is collapsed.
38927 * @param {Roo.LayoutRegion} this
38929 "collapsed" : true,
38932 * Fires when this region is expanded.
38933 * @param {Roo.LayoutRegion} this
38938 * Fires when this region is slid into view.
38939 * @param {Roo.LayoutRegion} this
38941 "slideshow" : true,
38944 * Fires when this region slides out of view.
38945 * @param {Roo.LayoutRegion} this
38947 "slidehide" : true,
38949 * @event panelactivated
38950 * Fires when a panel is activated.
38951 * @param {Roo.LayoutRegion} this
38952 * @param {Roo.ContentPanel} panel The activated panel
38954 "panelactivated" : true,
38957 * Fires when the user resizes this region.
38958 * @param {Roo.LayoutRegion} this
38959 * @param {Number} newSize The new size (width for east/west, height for north/south)
38963 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38964 this.panels = new Roo.util.MixedCollection();
38965 this.panels.getKey = this.getPanelId.createDelegate(this);
38967 this.activePanel = null;
38968 // ensure listeners are added...
38970 if (config.listeners || config.events) {
38971 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38972 listeners : config.listeners || {},
38973 events : config.events || {}
38977 if(skipConfig !== true){
38978 this.applyConfig(config);
38982 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38984 getPanelId : function(p){
38988 applyConfig : function(config){
38989 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38990 this.config = config;
38995 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38996 * the width, for horizontal (north, south) the height.
38997 * @param {Number} newSize The new width or height
38999 resizeTo : function(newSize){
39000 var el = this.el ? this.el :
39001 (this.activePanel ? this.activePanel.getEl() : null);
39003 switch(this.position){
39006 el.setWidth(newSize);
39007 this.fireEvent("resized", this, newSize);
39011 el.setHeight(newSize);
39012 this.fireEvent("resized", this, newSize);
39018 getBox : function(){
39019 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39022 getMargins : function(){
39023 return this.margins;
39026 updateBox : function(box){
39028 var el = this.activePanel.getEl();
39029 el.dom.style.left = box.x + "px";
39030 el.dom.style.top = box.y + "px";
39031 this.activePanel.setSize(box.width, box.height);
39035 * Returns the container element for this region.
39036 * @return {Roo.Element}
39038 getEl : function(){
39039 return this.activePanel;
39043 * Returns true if this region is currently visible.
39044 * @return {Boolean}
39046 isVisible : function(){
39047 return this.activePanel ? true : false;
39050 setActivePanel : function(panel){
39051 panel = this.getPanel(panel);
39052 if(this.activePanel && this.activePanel != panel){
39053 this.activePanel.setActiveState(false);
39054 this.activePanel.getEl().setLeftTop(-10000,-10000);
39056 this.activePanel = panel;
39057 panel.setActiveState(true);
39059 panel.setSize(this.box.width, this.box.height);
39061 this.fireEvent("panelactivated", this, panel);
39062 this.fireEvent("invalidated");
39066 * Show the specified panel.
39067 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39068 * @return {Roo.ContentPanel} The shown panel or null
39070 showPanel : function(panel){
39071 panel = this.getPanel(panel);
39073 this.setActivePanel(panel);
39079 * Get the active panel for this region.
39080 * @return {Roo.ContentPanel} The active panel or null
39082 getActivePanel : function(){
39083 return this.activePanel;
39087 * Add the passed ContentPanel(s)
39088 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39089 * @return {Roo.ContentPanel} The panel added (if only one was added)
39091 add : function(panel){
39092 if(arguments.length > 1){
39093 for(var i = 0, len = arguments.length; i < len; i++) {
39094 this.add(arguments[i]);
39098 if(this.hasPanel(panel)){
39099 this.showPanel(panel);
39102 var el = panel.getEl();
39103 if(el.dom.parentNode != this.mgr.el.dom){
39104 this.mgr.el.dom.appendChild(el.dom);
39106 if(panel.setRegion){
39107 panel.setRegion(this);
39109 this.panels.add(panel);
39110 el.setStyle("position", "absolute");
39111 if(!panel.background){
39112 this.setActivePanel(panel);
39113 if(this.config.initialSize && this.panels.getCount()==1){
39114 this.resizeTo(this.config.initialSize);
39117 this.fireEvent("paneladded", this, panel);
39122 * Returns true if the panel is in this region.
39123 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39124 * @return {Boolean}
39126 hasPanel : function(panel){
39127 if(typeof panel == "object"){ // must be panel obj
39128 panel = panel.getId();
39130 return this.getPanel(panel) ? true : false;
39134 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39135 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39136 * @param {Boolean} preservePanel Overrides the config preservePanel option
39137 * @return {Roo.ContentPanel} The panel that was removed
39139 remove : function(panel, preservePanel){
39140 panel = this.getPanel(panel);
39145 this.fireEvent("beforeremove", this, panel, e);
39146 if(e.cancel === true){
39149 var panelId = panel.getId();
39150 this.panels.removeKey(panelId);
39155 * Returns the panel specified or null if it's not in this region.
39156 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39157 * @return {Roo.ContentPanel}
39159 getPanel : function(id){
39160 if(typeof id == "object"){ // must be panel obj
39163 return this.panels.get(id);
39167 * Returns this regions position (north/south/east/west/center).
39170 getPosition: function(){
39171 return this.position;
39175 * Ext JS Library 1.1.1
39176 * Copyright(c) 2006-2007, Ext JS, LLC.
39178 * Originally Released Under LGPL - original licence link has changed is not relivant.
39181 * <script type="text/javascript">
39185 * @class Roo.bootstrap.layout.Region
39186 * @extends Roo.bootstrap.layout.Basic
39187 * This class represents a region in a layout manager.
39189 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39190 * @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})
39191 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39192 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39193 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39194 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39195 * @cfg {String} title The title for the region (overrides panel titles)
39196 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39197 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39198 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39199 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39200 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39201 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39202 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39203 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39204 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39205 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39207 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39208 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39209 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39210 * @cfg {Number} width For East/West panels
39211 * @cfg {Number} height For North/South panels
39212 * @cfg {Boolean} split To show the splitter
39213 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39215 * @cfg {string} cls Extra CSS classes to add to region
39217 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39218 * @cfg {string} region the region that it inhabits..
39221 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39222 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39224 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39225 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39226 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39228 Roo.bootstrap.layout.Region = function(config)
39230 this.applyConfig(config);
39232 var mgr = config.mgr;
39233 var pos = config.region;
39234 config.skipConfig = true;
39235 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39238 this.onRender(mgr.el);
39241 this.visible = true;
39242 this.collapsed = false;
39243 this.unrendered_panels = [];
39246 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39248 position: '', // set by wrapper (eg. north/south etc..)
39249 unrendered_panels : null, // unrendered panels.
39251 tabPosition : false,
39253 mgr: false, // points to 'Border'
39256 createBody : function(){
39257 /** This region's body element
39258 * @type Roo.Element */
39259 this.bodyEl = this.el.createChild({
39261 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39265 onRender: function(ctr, pos)
39267 var dh = Roo.DomHelper;
39268 /** This region's container element
39269 * @type Roo.Element */
39270 this.el = dh.append(ctr.dom, {
39272 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39274 /** This region's title element
39275 * @type Roo.Element */
39277 this.titleEl = dh.append(this.el.dom, {
39279 unselectable: "on",
39280 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39282 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39283 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39287 this.titleEl.enableDisplayMode();
39288 /** This region's title text element
39289 * @type HTMLElement */
39290 this.titleTextEl = this.titleEl.dom.firstChild;
39291 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39293 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39294 this.closeBtn.enableDisplayMode();
39295 this.closeBtn.on("click", this.closeClicked, this);
39296 this.closeBtn.hide();
39298 this.createBody(this.config);
39299 if(this.config.hideWhenEmpty){
39301 this.on("paneladded", this.validateVisibility, this);
39302 this.on("panelremoved", this.validateVisibility, this);
39304 if(this.autoScroll){
39305 this.bodyEl.setStyle("overflow", "auto");
39307 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39309 //if(c.titlebar !== false){
39310 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39311 this.titleEl.hide();
39313 this.titleEl.show();
39314 if(this.config.title){
39315 this.titleTextEl.innerHTML = this.config.title;
39319 if(this.config.collapsed){
39320 this.collapse(true);
39322 if(this.config.hidden){
39326 if (this.unrendered_panels && this.unrendered_panels.length) {
39327 for (var i =0;i< this.unrendered_panels.length; i++) {
39328 this.add(this.unrendered_panels[i]);
39330 this.unrendered_panels = null;
39336 applyConfig : function(c)
39339 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39340 var dh = Roo.DomHelper;
39341 if(c.titlebar !== false){
39342 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39343 this.collapseBtn.on("click", this.collapse, this);
39344 this.collapseBtn.enableDisplayMode();
39346 if(c.showPin === true || this.showPin){
39347 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39348 this.stickBtn.enableDisplayMode();
39349 this.stickBtn.on("click", this.expand, this);
39350 this.stickBtn.hide();
39355 /** This region's collapsed element
39356 * @type Roo.Element */
39359 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39360 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39363 if(c.floatable !== false){
39364 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39365 this.collapsedEl.on("click", this.collapseClick, this);
39368 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39369 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39370 id: "message", unselectable: "on", style:{"float":"left"}});
39371 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39373 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39374 this.expandBtn.on("click", this.expand, this);
39378 if(this.collapseBtn){
39379 this.collapseBtn.setVisible(c.collapsible == true);
39382 this.cmargins = c.cmargins || this.cmargins ||
39383 (this.position == "west" || this.position == "east" ?
39384 {top: 0, left: 2, right:2, bottom: 0} :
39385 {top: 2, left: 0, right:0, bottom: 2});
39387 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39390 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39392 this.autoScroll = c.autoScroll || false;
39397 this.duration = c.duration || .30;
39398 this.slideDuration = c.slideDuration || .45;
39403 * Returns true if this region is currently visible.
39404 * @return {Boolean}
39406 isVisible : function(){
39407 return this.visible;
39411 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39412 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39414 //setCollapsedTitle : function(title){
39415 // title = title || " ";
39416 // if(this.collapsedTitleTextEl){
39417 // this.collapsedTitleTextEl.innerHTML = title;
39421 getBox : function(){
39423 // if(!this.collapsed){
39424 b = this.el.getBox(false, true);
39426 // b = this.collapsedEl.getBox(false, true);
39431 getMargins : function(){
39432 return this.margins;
39433 //return this.collapsed ? this.cmargins : this.margins;
39436 highlight : function(){
39437 this.el.addClass("x-layout-panel-dragover");
39440 unhighlight : function(){
39441 this.el.removeClass("x-layout-panel-dragover");
39444 updateBox : function(box)
39446 if (!this.bodyEl) {
39447 return; // not rendered yet..
39451 if(!this.collapsed){
39452 this.el.dom.style.left = box.x + "px";
39453 this.el.dom.style.top = box.y + "px";
39454 this.updateBody(box.width, box.height);
39456 this.collapsedEl.dom.style.left = box.x + "px";
39457 this.collapsedEl.dom.style.top = box.y + "px";
39458 this.collapsedEl.setSize(box.width, box.height);
39461 this.tabs.autoSizeTabs();
39465 updateBody : function(w, h)
39468 this.el.setWidth(w);
39469 w -= this.el.getBorderWidth("rl");
39470 if(this.config.adjustments){
39471 w += this.config.adjustments[0];
39474 if(h !== null && h > 0){
39475 this.el.setHeight(h);
39476 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39477 h -= this.el.getBorderWidth("tb");
39478 if(this.config.adjustments){
39479 h += this.config.adjustments[1];
39481 this.bodyEl.setHeight(h);
39483 h = this.tabs.syncHeight(h);
39486 if(this.panelSize){
39487 w = w !== null ? w : this.panelSize.width;
39488 h = h !== null ? h : this.panelSize.height;
39490 if(this.activePanel){
39491 var el = this.activePanel.getEl();
39492 w = w !== null ? w : el.getWidth();
39493 h = h !== null ? h : el.getHeight();
39494 this.panelSize = {width: w, height: h};
39495 this.activePanel.setSize(w, h);
39497 if(Roo.isIE && this.tabs){
39498 this.tabs.el.repaint();
39503 * Returns the container element for this region.
39504 * @return {Roo.Element}
39506 getEl : function(){
39511 * Hides this region.
39514 //if(!this.collapsed){
39515 this.el.dom.style.left = "-2000px";
39518 // this.collapsedEl.dom.style.left = "-2000px";
39519 // this.collapsedEl.hide();
39521 this.visible = false;
39522 this.fireEvent("visibilitychange", this, false);
39526 * Shows this region if it was previously hidden.
39529 //if(!this.collapsed){
39532 // this.collapsedEl.show();
39534 this.visible = true;
39535 this.fireEvent("visibilitychange", this, true);
39538 closeClicked : function(){
39539 if(this.activePanel){
39540 this.remove(this.activePanel);
39544 collapseClick : function(e){
39546 e.stopPropagation();
39549 e.stopPropagation();
39555 * Collapses this region.
39556 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39559 collapse : function(skipAnim, skipCheck = false){
39560 if(this.collapsed) {
39564 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39566 this.collapsed = true;
39568 this.split.el.hide();
39570 if(this.config.animate && skipAnim !== true){
39571 this.fireEvent("invalidated", this);
39572 this.animateCollapse();
39574 this.el.setLocation(-20000,-20000);
39576 this.collapsedEl.show();
39577 this.fireEvent("collapsed", this);
39578 this.fireEvent("invalidated", this);
39584 animateCollapse : function(){
39589 * Expands this region if it was previously collapsed.
39590 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39591 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39594 expand : function(e, skipAnim){
39596 e.stopPropagation();
39598 if(!this.collapsed || this.el.hasActiveFx()) {
39602 this.afterSlideIn();
39605 this.collapsed = false;
39606 if(this.config.animate && skipAnim !== true){
39607 this.animateExpand();
39611 this.split.el.show();
39613 this.collapsedEl.setLocation(-2000,-2000);
39614 this.collapsedEl.hide();
39615 this.fireEvent("invalidated", this);
39616 this.fireEvent("expanded", this);
39620 animateExpand : function(){
39624 initTabs : function()
39626 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39628 var ts = new Roo.bootstrap.panel.Tabs({
39629 el: this.bodyEl.dom,
39631 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39632 disableTooltips: this.config.disableTabTips,
39633 toolbar : this.config.toolbar
39636 if(this.config.hideTabs){
39637 ts.stripWrap.setDisplayed(false);
39640 ts.resizeTabs = this.config.resizeTabs === true;
39641 ts.minTabWidth = this.config.minTabWidth || 40;
39642 ts.maxTabWidth = this.config.maxTabWidth || 250;
39643 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39644 ts.monitorResize = false;
39645 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39646 ts.bodyEl.addClass('roo-layout-tabs-body');
39647 this.panels.each(this.initPanelAsTab, this);
39650 initPanelAsTab : function(panel){
39651 var ti = this.tabs.addTab(
39655 this.config.closeOnTab && panel.isClosable(),
39658 if(panel.tabTip !== undefined){
39659 ti.setTooltip(panel.tabTip);
39661 ti.on("activate", function(){
39662 this.setActivePanel(panel);
39665 if(this.config.closeOnTab){
39666 ti.on("beforeclose", function(t, e){
39668 this.remove(panel);
39672 panel.tabItem = ti;
39677 updatePanelTitle : function(panel, title)
39679 if(this.activePanel == panel){
39680 this.updateTitle(title);
39683 var ti = this.tabs.getTab(panel.getEl().id);
39685 if(panel.tabTip !== undefined){
39686 ti.setTooltip(panel.tabTip);
39691 updateTitle : function(title){
39692 if(this.titleTextEl && !this.config.title){
39693 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39697 setActivePanel : function(panel)
39699 panel = this.getPanel(panel);
39700 if(this.activePanel && this.activePanel != panel){
39701 if(this.activePanel.setActiveState(false) === false){
39705 this.activePanel = panel;
39706 panel.setActiveState(true);
39707 if(this.panelSize){
39708 panel.setSize(this.panelSize.width, this.panelSize.height);
39711 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39713 this.updateTitle(panel.getTitle());
39715 this.fireEvent("invalidated", this);
39717 this.fireEvent("panelactivated", this, panel);
39721 * Shows the specified panel.
39722 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39723 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39725 showPanel : function(panel)
39727 panel = this.getPanel(panel);
39730 var tab = this.tabs.getTab(panel.getEl().id);
39731 if(tab.isHidden()){
39732 this.tabs.unhideTab(tab.id);
39736 this.setActivePanel(panel);
39743 * Get the active panel for this region.
39744 * @return {Roo.ContentPanel} The active panel or null
39746 getActivePanel : function(){
39747 return this.activePanel;
39750 validateVisibility : function(){
39751 if(this.panels.getCount() < 1){
39752 this.updateTitle(" ");
39753 this.closeBtn.hide();
39756 if(!this.isVisible()){
39763 * Adds the passed ContentPanel(s) to this region.
39764 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39765 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39767 add : function(panel)
39769 if(arguments.length > 1){
39770 for(var i = 0, len = arguments.length; i < len; i++) {
39771 this.add(arguments[i]);
39776 // if we have not been rendered yet, then we can not really do much of this..
39777 if (!this.bodyEl) {
39778 this.unrendered_panels.push(panel);
39785 if(this.hasPanel(panel)){
39786 this.showPanel(panel);
39789 panel.setRegion(this);
39790 this.panels.add(panel);
39791 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39792 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39793 // and hide them... ???
39794 this.bodyEl.dom.appendChild(panel.getEl().dom);
39795 if(panel.background !== true){
39796 this.setActivePanel(panel);
39798 this.fireEvent("paneladded", this, panel);
39805 this.initPanelAsTab(panel);
39809 if(panel.background !== true){
39810 this.tabs.activate(panel.getEl().id);
39812 this.fireEvent("paneladded", this, panel);
39817 * Hides the tab for the specified panel.
39818 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39820 hidePanel : function(panel){
39821 if(this.tabs && (panel = this.getPanel(panel))){
39822 this.tabs.hideTab(panel.getEl().id);
39827 * Unhides the tab for a previously hidden panel.
39828 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39830 unhidePanel : function(panel){
39831 if(this.tabs && (panel = this.getPanel(panel))){
39832 this.tabs.unhideTab(panel.getEl().id);
39836 clearPanels : function(){
39837 while(this.panels.getCount() > 0){
39838 this.remove(this.panels.first());
39843 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39844 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39845 * @param {Boolean} preservePanel Overrides the config preservePanel option
39846 * @return {Roo.ContentPanel} The panel that was removed
39848 remove : function(panel, preservePanel)
39850 panel = this.getPanel(panel);
39855 this.fireEvent("beforeremove", this, panel, e);
39856 if(e.cancel === true){
39859 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39860 var panelId = panel.getId();
39861 this.panels.removeKey(panelId);
39863 document.body.appendChild(panel.getEl().dom);
39866 this.tabs.removeTab(panel.getEl().id);
39867 }else if (!preservePanel){
39868 this.bodyEl.dom.removeChild(panel.getEl().dom);
39870 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39871 var p = this.panels.first();
39872 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39873 tempEl.appendChild(p.getEl().dom);
39874 this.bodyEl.update("");
39875 this.bodyEl.dom.appendChild(p.getEl().dom);
39877 this.updateTitle(p.getTitle());
39879 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39880 this.setActivePanel(p);
39882 panel.setRegion(null);
39883 if(this.activePanel == panel){
39884 this.activePanel = null;
39886 if(this.config.autoDestroy !== false && preservePanel !== true){
39887 try{panel.destroy();}catch(e){}
39889 this.fireEvent("panelremoved", this, panel);
39894 * Returns the TabPanel component used by this region
39895 * @return {Roo.TabPanel}
39897 getTabs : function(){
39901 createTool : function(parentEl, className){
39902 var btn = Roo.DomHelper.append(parentEl, {
39904 cls: "x-layout-tools-button",
39907 cls: "roo-layout-tools-button-inner " + className,
39911 btn.addClassOnOver("roo-layout-tools-button-over");
39916 * Ext JS Library 1.1.1
39917 * Copyright(c) 2006-2007, Ext JS, LLC.
39919 * Originally Released Under LGPL - original licence link has changed is not relivant.
39922 * <script type="text/javascript">
39928 * @class Roo.SplitLayoutRegion
39929 * @extends Roo.LayoutRegion
39930 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39932 Roo.bootstrap.layout.Split = function(config){
39933 this.cursor = config.cursor;
39934 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39937 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39939 splitTip : "Drag to resize.",
39940 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39941 useSplitTips : false,
39943 applyConfig : function(config){
39944 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39947 onRender : function(ctr,pos) {
39949 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39950 if(!this.config.split){
39955 var splitEl = Roo.DomHelper.append(ctr.dom, {
39957 id: this.el.id + "-split",
39958 cls: "roo-layout-split roo-layout-split-"+this.position,
39961 /** The SplitBar for this region
39962 * @type Roo.SplitBar */
39963 // does not exist yet...
39964 Roo.log([this.position, this.orientation]);
39966 this.split = new Roo.bootstrap.SplitBar({
39967 dragElement : splitEl,
39968 resizingElement: this.el,
39969 orientation : this.orientation
39972 this.split.on("moved", this.onSplitMove, this);
39973 this.split.useShim = this.config.useShim === true;
39974 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39975 if(this.useSplitTips){
39976 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39978 //if(config.collapsible){
39979 // this.split.el.on("dblclick", this.collapse, this);
39982 if(typeof this.config.minSize != "undefined"){
39983 this.split.minSize = this.config.minSize;
39985 if(typeof this.config.maxSize != "undefined"){
39986 this.split.maxSize = this.config.maxSize;
39988 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39989 this.hideSplitter();
39994 getHMaxSize : function(){
39995 var cmax = this.config.maxSize || 10000;
39996 var center = this.mgr.getRegion("center");
39997 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40000 getVMaxSize : function(){
40001 var cmax = this.config.maxSize || 10000;
40002 var center = this.mgr.getRegion("center");
40003 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40006 onSplitMove : function(split, newSize){
40007 this.fireEvent("resized", this, newSize);
40011 * Returns the {@link Roo.SplitBar} for this region.
40012 * @return {Roo.SplitBar}
40014 getSplitBar : function(){
40019 this.hideSplitter();
40020 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40023 hideSplitter : function(){
40025 this.split.el.setLocation(-2000,-2000);
40026 this.split.el.hide();
40032 this.split.el.show();
40034 Roo.bootstrap.layout.Split.superclass.show.call(this);
40037 beforeSlide: function(){
40038 if(Roo.isGecko){// firefox overflow auto bug workaround
40039 this.bodyEl.clip();
40041 this.tabs.bodyEl.clip();
40043 if(this.activePanel){
40044 this.activePanel.getEl().clip();
40046 if(this.activePanel.beforeSlide){
40047 this.activePanel.beforeSlide();
40053 afterSlide : function(){
40054 if(Roo.isGecko){// firefox overflow auto bug workaround
40055 this.bodyEl.unclip();
40057 this.tabs.bodyEl.unclip();
40059 if(this.activePanel){
40060 this.activePanel.getEl().unclip();
40061 if(this.activePanel.afterSlide){
40062 this.activePanel.afterSlide();
40068 initAutoHide : function(){
40069 if(this.autoHide !== false){
40070 if(!this.autoHideHd){
40071 var st = new Roo.util.DelayedTask(this.slideIn, this);
40072 this.autoHideHd = {
40073 "mouseout": function(e){
40074 if(!e.within(this.el, true)){
40078 "mouseover" : function(e){
40084 this.el.on(this.autoHideHd);
40088 clearAutoHide : function(){
40089 if(this.autoHide !== false){
40090 this.el.un("mouseout", this.autoHideHd.mouseout);
40091 this.el.un("mouseover", this.autoHideHd.mouseover);
40095 clearMonitor : function(){
40096 Roo.get(document).un("click", this.slideInIf, this);
40099 // these names are backwards but not changed for compat
40100 slideOut : function(){
40101 if(this.isSlid || this.el.hasActiveFx()){
40104 this.isSlid = true;
40105 if(this.collapseBtn){
40106 this.collapseBtn.hide();
40108 this.closeBtnState = this.closeBtn.getStyle('display');
40109 this.closeBtn.hide();
40111 this.stickBtn.show();
40114 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40115 this.beforeSlide();
40116 this.el.setStyle("z-index", 10001);
40117 this.el.slideIn(this.getSlideAnchor(), {
40118 callback: function(){
40120 this.initAutoHide();
40121 Roo.get(document).on("click", this.slideInIf, this);
40122 this.fireEvent("slideshow", this);
40129 afterSlideIn : function(){
40130 this.clearAutoHide();
40131 this.isSlid = false;
40132 this.clearMonitor();
40133 this.el.setStyle("z-index", "");
40134 if(this.collapseBtn){
40135 this.collapseBtn.show();
40137 this.closeBtn.setStyle('display', this.closeBtnState);
40139 this.stickBtn.hide();
40141 this.fireEvent("slidehide", this);
40144 slideIn : function(cb){
40145 if(!this.isSlid || this.el.hasActiveFx()){
40149 this.isSlid = false;
40150 this.beforeSlide();
40151 this.el.slideOut(this.getSlideAnchor(), {
40152 callback: function(){
40153 this.el.setLeftTop(-10000, -10000);
40155 this.afterSlideIn();
40163 slideInIf : function(e){
40164 if(!e.within(this.el)){
40169 animateCollapse : function(){
40170 this.beforeSlide();
40171 this.el.setStyle("z-index", 20000);
40172 var anchor = this.getSlideAnchor();
40173 this.el.slideOut(anchor, {
40174 callback : function(){
40175 this.el.setStyle("z-index", "");
40176 this.collapsedEl.slideIn(anchor, {duration:.3});
40178 this.el.setLocation(-10000,-10000);
40180 this.fireEvent("collapsed", this);
40187 animateExpand : function(){
40188 this.beforeSlide();
40189 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40190 this.el.setStyle("z-index", 20000);
40191 this.collapsedEl.hide({
40194 this.el.slideIn(this.getSlideAnchor(), {
40195 callback : function(){
40196 this.el.setStyle("z-index", "");
40199 this.split.el.show();
40201 this.fireEvent("invalidated", this);
40202 this.fireEvent("expanded", this);
40230 getAnchor : function(){
40231 return this.anchors[this.position];
40234 getCollapseAnchor : function(){
40235 return this.canchors[this.position];
40238 getSlideAnchor : function(){
40239 return this.sanchors[this.position];
40242 getAlignAdj : function(){
40243 var cm = this.cmargins;
40244 switch(this.position){
40260 getExpandAdj : function(){
40261 var c = this.collapsedEl, cm = this.cmargins;
40262 switch(this.position){
40264 return [-(cm.right+c.getWidth()+cm.left), 0];
40267 return [cm.right+c.getWidth()+cm.left, 0];
40270 return [0, -(cm.top+cm.bottom+c.getHeight())];
40273 return [0, cm.top+cm.bottom+c.getHeight()];
40279 * Ext JS Library 1.1.1
40280 * Copyright(c) 2006-2007, Ext JS, LLC.
40282 * Originally Released Under LGPL - original licence link has changed is not relivant.
40285 * <script type="text/javascript">
40288 * These classes are private internal classes
40290 Roo.bootstrap.layout.Center = function(config){
40291 config.region = "center";
40292 Roo.bootstrap.layout.Region.call(this, config);
40293 this.visible = true;
40294 this.minWidth = config.minWidth || 20;
40295 this.minHeight = config.minHeight || 20;
40298 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40300 // center panel can't be hidden
40304 // center panel can't be hidden
40307 getMinWidth: function(){
40308 return this.minWidth;
40311 getMinHeight: function(){
40312 return this.minHeight;
40326 Roo.bootstrap.layout.North = function(config)
40328 config.region = 'north';
40329 config.cursor = 'n-resize';
40331 Roo.bootstrap.layout.Split.call(this, config);
40335 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40336 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40337 this.split.el.addClass("roo-layout-split-v");
40339 //var size = config.initialSize || config.height;
40340 //if(this.el && typeof size != "undefined"){
40341 // this.el.setHeight(size);
40344 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40346 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40349 onRender : function(ctr, pos)
40351 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40352 var size = this.config.initialSize || this.config.height;
40353 if(this.el && typeof size != "undefined"){
40354 this.el.setHeight(size);
40359 getBox : function(){
40360 if(this.collapsed){
40361 return this.collapsedEl.getBox();
40363 var box = this.el.getBox();
40365 box.height += this.split.el.getHeight();
40370 updateBox : function(box){
40371 if(this.split && !this.collapsed){
40372 box.height -= this.split.el.getHeight();
40373 this.split.el.setLeft(box.x);
40374 this.split.el.setTop(box.y+box.height);
40375 this.split.el.setWidth(box.width);
40377 if(this.collapsed){
40378 this.updateBody(box.width, null);
40380 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40388 Roo.bootstrap.layout.South = function(config){
40389 config.region = 'south';
40390 config.cursor = 's-resize';
40391 Roo.bootstrap.layout.Split.call(this, config);
40393 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40394 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40395 this.split.el.addClass("roo-layout-split-v");
40400 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40401 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40403 onRender : function(ctr, pos)
40405 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40406 var size = this.config.initialSize || this.config.height;
40407 if(this.el && typeof size != "undefined"){
40408 this.el.setHeight(size);
40413 getBox : function(){
40414 if(this.collapsed){
40415 return this.collapsedEl.getBox();
40417 var box = this.el.getBox();
40419 var sh = this.split.el.getHeight();
40426 updateBox : function(box){
40427 if(this.split && !this.collapsed){
40428 var sh = this.split.el.getHeight();
40431 this.split.el.setLeft(box.x);
40432 this.split.el.setTop(box.y-sh);
40433 this.split.el.setWidth(box.width);
40435 if(this.collapsed){
40436 this.updateBody(box.width, null);
40438 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40442 Roo.bootstrap.layout.East = function(config){
40443 config.region = "east";
40444 config.cursor = "e-resize";
40445 Roo.bootstrap.layout.Split.call(this, config);
40447 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40448 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40449 this.split.el.addClass("roo-layout-split-h");
40453 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40454 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40456 onRender : function(ctr, pos)
40458 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40459 var size = this.config.initialSize || this.config.width;
40460 if(this.el && typeof size != "undefined"){
40461 this.el.setWidth(size);
40466 getBox : function(){
40467 if(this.collapsed){
40468 return this.collapsedEl.getBox();
40470 var box = this.el.getBox();
40472 var sw = this.split.el.getWidth();
40479 updateBox : function(box){
40480 if(this.split && !this.collapsed){
40481 var sw = this.split.el.getWidth();
40483 this.split.el.setLeft(box.x);
40484 this.split.el.setTop(box.y);
40485 this.split.el.setHeight(box.height);
40488 if(this.collapsed){
40489 this.updateBody(null, box.height);
40491 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40495 Roo.bootstrap.layout.West = function(config){
40496 config.region = "west";
40497 config.cursor = "w-resize";
40499 Roo.bootstrap.layout.Split.call(this, config);
40501 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40502 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40503 this.split.el.addClass("roo-layout-split-h");
40507 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40508 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40510 onRender: function(ctr, pos)
40512 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40513 var size = this.config.initialSize || this.config.width;
40514 if(typeof size != "undefined"){
40515 this.el.setWidth(size);
40519 getBox : function(){
40520 if(this.collapsed){
40521 return this.collapsedEl.getBox();
40523 var box = this.el.getBox();
40524 if (box.width == 0) {
40525 box.width = this.config.width; // kludge?
40528 box.width += this.split.el.getWidth();
40533 updateBox : function(box){
40534 if(this.split && !this.collapsed){
40535 var sw = this.split.el.getWidth();
40537 this.split.el.setLeft(box.x+box.width);
40538 this.split.el.setTop(box.y);
40539 this.split.el.setHeight(box.height);
40541 if(this.collapsed){
40542 this.updateBody(null, box.height);
40544 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40546 });Roo.namespace("Roo.bootstrap.panel");/*
40548 * Ext JS Library 1.1.1
40549 * Copyright(c) 2006-2007, Ext JS, LLC.
40551 * Originally Released Under LGPL - original licence link has changed is not relivant.
40554 * <script type="text/javascript">
40557 * @class Roo.ContentPanel
40558 * @extends Roo.util.Observable
40560 * A basic ContentPanel element.
40561 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40562 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40563 * @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
40564 * @cfg {Boolean} closable True if the panel can be closed/removed
40565 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40566 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40567 * @cfg {Toolbar} toolbar A toolbar for this panel
40568 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40569 * @cfg {String} title The title for this panel
40570 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40571 * @cfg {String} url Calls {@link #setUrl} with this value
40572 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40573 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40574 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40575 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40576 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40577 * @cfg {Boolean} badges render the badges
40578 * @cfg {String} cls extra classes to use
40579 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40582 * Create a new ContentPanel.
40583 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40584 * @param {String/Object} config A string to set only the title or a config object
40585 * @param {String} content (optional) Set the HTML content for this panel
40586 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40588 Roo.bootstrap.panel.Content = function( config){
40590 this.tpl = config.tpl || false;
40592 var el = config.el;
40593 var content = config.content;
40595 if(config.autoCreate){ // xtype is available if this is called from factory
40598 this.el = Roo.get(el);
40599 if(!this.el && config && config.autoCreate){
40600 if(typeof config.autoCreate == "object"){
40601 if(!config.autoCreate.id){
40602 config.autoCreate.id = config.id||el;
40604 this.el = Roo.DomHelper.append(document.body,
40605 config.autoCreate, true);
40609 cls: (config.cls || '') +
40610 (config.background ? ' bg-' + config.background : '') +
40611 " roo-layout-inactive-content",
40614 if (config.iframe) {
40618 style : 'border: 0px',
40619 src : 'about:blank'
40625 elcfg.html = config.html;
40629 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40630 if (config.iframe) {
40631 this.iframeEl = this.el.select('iframe',true).first();
40636 this.closable = false;
40637 this.loaded = false;
40638 this.active = false;
40641 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40643 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40645 this.wrapEl = this.el; //this.el.wrap();
40647 if (config.toolbar.items) {
40648 ti = config.toolbar.items ;
40649 delete config.toolbar.items ;
40653 this.toolbar.render(this.wrapEl, 'before');
40654 for(var i =0;i < ti.length;i++) {
40655 // Roo.log(['add child', items[i]]);
40656 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40658 this.toolbar.items = nitems;
40659 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40660 delete config.toolbar;
40664 // xtype created footer. - not sure if will work as we normally have to render first..
40665 if (this.footer && !this.footer.el && this.footer.xtype) {
40666 if (!this.wrapEl) {
40667 this.wrapEl = this.el.wrap();
40670 this.footer.container = this.wrapEl.createChild();
40672 this.footer = Roo.factory(this.footer, Roo);
40677 if(typeof config == "string"){
40678 this.title = config;
40680 Roo.apply(this, config);
40684 this.resizeEl = Roo.get(this.resizeEl, true);
40686 this.resizeEl = this.el;
40688 // handle view.xtype
40696 * Fires when this panel is activated.
40697 * @param {Roo.ContentPanel} this
40701 * @event deactivate
40702 * Fires when this panel is activated.
40703 * @param {Roo.ContentPanel} this
40705 "deactivate" : true,
40709 * Fires when this panel is resized if fitToFrame is true.
40710 * @param {Roo.ContentPanel} this
40711 * @param {Number} width The width after any component adjustments
40712 * @param {Number} height The height after any component adjustments
40718 * Fires when this tab is created
40719 * @param {Roo.ContentPanel} this
40725 * Fires when this content is scrolled
40726 * @param {Roo.ContentPanel} this
40727 * @param {Event} scrollEvent
40738 if(this.autoScroll && !this.iframe){
40739 this.resizeEl.setStyle("overflow", "auto");
40740 this.resizeEl.on('scroll', this.onScroll, this);
40742 // fix randome scrolling
40743 //this.el.on('scroll', function() {
40744 // Roo.log('fix random scolling');
40745 // this.scrollTo('top',0);
40748 content = content || this.content;
40750 this.setContent(content);
40752 if(config && config.url){
40753 this.setUrl(this.url, this.params, this.loadOnce);
40758 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40760 if (this.view && typeof(this.view.xtype) != 'undefined') {
40761 this.view.el = this.el.appendChild(document.createElement("div"));
40762 this.view = Roo.factory(this.view);
40763 this.view.render && this.view.render(false, '');
40767 this.fireEvent('render', this);
40770 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40780 /* Resize Element - use this to work out scroll etc. */
40783 setRegion : function(region){
40784 this.region = region;
40785 this.setActiveClass(region && !this.background);
40789 setActiveClass: function(state)
40792 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40793 this.el.setStyle('position','relative');
40795 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40796 this.el.setStyle('position', 'absolute');
40801 * Returns the toolbar for this Panel if one was configured.
40802 * @return {Roo.Toolbar}
40804 getToolbar : function(){
40805 return this.toolbar;
40808 setActiveState : function(active)
40810 this.active = active;
40811 this.setActiveClass(active);
40813 if(this.fireEvent("deactivate", this) === false){
40818 this.fireEvent("activate", this);
40822 * Updates this panel's element (not for iframe)
40823 * @param {String} content The new content
40824 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40826 setContent : function(content, loadScripts){
40831 this.el.update(content, loadScripts);
40834 ignoreResize : function(w, h){
40835 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40838 this.lastSize = {width: w, height: h};
40843 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40844 * @return {Roo.UpdateManager} The UpdateManager
40846 getUpdateManager : function(){
40850 return this.el.getUpdateManager();
40853 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40854 * Does not work with IFRAME contents
40855 * @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:
40858 url: "your-url.php",
40859 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40860 callback: yourFunction,
40861 scope: yourObject, //(optional scope)
40864 text: "Loading...",
40870 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40871 * 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.
40872 * @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}
40873 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40874 * @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.
40875 * @return {Roo.ContentPanel} this
40883 var um = this.el.getUpdateManager();
40884 um.update.apply(um, arguments);
40890 * 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.
40891 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40892 * @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)
40893 * @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)
40894 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40896 setUrl : function(url, params, loadOnce){
40898 this.iframeEl.dom.src = url;
40902 if(this.refreshDelegate){
40903 this.removeListener("activate", this.refreshDelegate);
40905 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40906 this.on("activate", this.refreshDelegate);
40907 return this.el.getUpdateManager();
40910 _handleRefresh : function(url, params, loadOnce){
40911 if(!loadOnce || !this.loaded){
40912 var updater = this.el.getUpdateManager();
40913 updater.update(url, params, this._setLoaded.createDelegate(this));
40917 _setLoaded : function(){
40918 this.loaded = true;
40922 * Returns this panel's id
40925 getId : function(){
40930 * Returns this panel's element - used by regiosn to add.
40931 * @return {Roo.Element}
40933 getEl : function(){
40934 return this.wrapEl || this.el;
40939 adjustForComponents : function(width, height)
40941 //Roo.log('adjustForComponents ');
40942 if(this.resizeEl != this.el){
40943 width -= this.el.getFrameWidth('lr');
40944 height -= this.el.getFrameWidth('tb');
40947 var te = this.toolbar.getEl();
40948 te.setWidth(width);
40949 height -= te.getHeight();
40952 var te = this.footer.getEl();
40953 te.setWidth(width);
40954 height -= te.getHeight();
40958 if(this.adjustments){
40959 width += this.adjustments[0];
40960 height += this.adjustments[1];
40962 return {"width": width, "height": height};
40965 setSize : function(width, height){
40966 if(this.fitToFrame && !this.ignoreResize(width, height)){
40967 if(this.fitContainer && this.resizeEl != this.el){
40968 this.el.setSize(width, height);
40970 var size = this.adjustForComponents(width, height);
40972 this.iframeEl.setSize(width,height);
40975 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40976 this.fireEvent('resize', this, size.width, size.height);
40983 * Returns this panel's title
40986 getTitle : function(){
40988 if (typeof(this.title) != 'object') {
40993 for (var k in this.title) {
40994 if (!this.title.hasOwnProperty(k)) {
40998 if (k.indexOf('-') >= 0) {
40999 var s = k.split('-');
41000 for (var i = 0; i<s.length; i++) {
41001 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41004 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41011 * Set this panel's title
41012 * @param {String} title
41014 setTitle : function(title){
41015 this.title = title;
41017 this.region.updatePanelTitle(this, title);
41022 * Returns true is this panel was configured to be closable
41023 * @return {Boolean}
41025 isClosable : function(){
41026 return this.closable;
41029 beforeSlide : function(){
41031 this.resizeEl.clip();
41034 afterSlide : function(){
41036 this.resizeEl.unclip();
41040 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41041 * Will fail silently if the {@link #setUrl} method has not been called.
41042 * This does not activate the panel, just updates its content.
41044 refresh : function(){
41045 if(this.refreshDelegate){
41046 this.loaded = false;
41047 this.refreshDelegate();
41052 * Destroys this panel
41054 destroy : function(){
41055 this.el.removeAllListeners();
41056 var tempEl = document.createElement("span");
41057 tempEl.appendChild(this.el.dom);
41058 tempEl.innerHTML = "";
41064 * form - if the content panel contains a form - this is a reference to it.
41065 * @type {Roo.form.Form}
41069 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41070 * This contains a reference to it.
41076 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41086 * @param {Object} cfg Xtype definition of item to add.
41090 getChildContainer: function () {
41091 return this.getEl();
41095 onScroll : function(e)
41097 this.fireEvent('scroll', this, e);
41102 var ret = new Roo.factory(cfg);
41107 if (cfg.xtype.match(/^Form$/)) {
41110 //if (this.footer) {
41111 // el = this.footer.container.insertSibling(false, 'before');
41113 el = this.el.createChild();
41116 this.form = new Roo.form.Form(cfg);
41119 if ( this.form.allItems.length) {
41120 this.form.render(el.dom);
41124 // should only have one of theses..
41125 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41126 // views.. should not be just added - used named prop 'view''
41128 cfg.el = this.el.appendChild(document.createElement("div"));
41131 var ret = new Roo.factory(cfg);
41133 ret.render && ret.render(false, ''); // render blank..
41143 * @class Roo.bootstrap.panel.Grid
41144 * @extends Roo.bootstrap.panel.Content
41146 * Create a new GridPanel.
41147 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41148 * @param {Object} config A the config object
41154 Roo.bootstrap.panel.Grid = function(config)
41158 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41159 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41161 config.el = this.wrapper;
41162 //this.el = this.wrapper;
41164 if (config.container) {
41165 // ctor'ed from a Border/panel.grid
41168 this.wrapper.setStyle("overflow", "hidden");
41169 this.wrapper.addClass('roo-grid-container');
41174 if(config.toolbar){
41175 var tool_el = this.wrapper.createChild();
41176 this.toolbar = Roo.factory(config.toolbar);
41178 if (config.toolbar.items) {
41179 ti = config.toolbar.items ;
41180 delete config.toolbar.items ;
41184 this.toolbar.render(tool_el);
41185 for(var i =0;i < ti.length;i++) {
41186 // Roo.log(['add child', items[i]]);
41187 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41189 this.toolbar.items = nitems;
41191 delete config.toolbar;
41194 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41195 config.grid.scrollBody = true;;
41196 config.grid.monitorWindowResize = false; // turn off autosizing
41197 config.grid.autoHeight = false;
41198 config.grid.autoWidth = false;
41200 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41202 if (config.background) {
41203 // render grid on panel activation (if panel background)
41204 this.on('activate', function(gp) {
41205 if (!gp.grid.rendered) {
41206 gp.grid.render(this.wrapper);
41207 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41212 this.grid.render(this.wrapper);
41213 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41216 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41217 // ??? needed ??? config.el = this.wrapper;
41222 // xtype created footer. - not sure if will work as we normally have to render first..
41223 if (this.footer && !this.footer.el && this.footer.xtype) {
41225 var ctr = this.grid.getView().getFooterPanel(true);
41226 this.footer.dataSource = this.grid.dataSource;
41227 this.footer = Roo.factory(this.footer, Roo);
41228 this.footer.render(ctr);
41238 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41239 getId : function(){
41240 return this.grid.id;
41244 * Returns the grid for this panel
41245 * @return {Roo.bootstrap.Table}
41247 getGrid : function(){
41251 setSize : function(width, height){
41252 if(!this.ignoreResize(width, height)){
41253 var grid = this.grid;
41254 var size = this.adjustForComponents(width, height);
41255 // tfoot is not a footer?
41258 var gridel = grid.getGridEl();
41259 gridel.setSize(size.width, size.height);
41261 var tbd = grid.getGridEl().select('tbody', true).first();
41262 var thd = grid.getGridEl().select('thead',true).first();
41263 var tbf= grid.getGridEl().select('tfoot', true).first();
41266 size.height -= tbf.getHeight();
41269 size.height -= thd.getHeight();
41272 tbd.setSize(size.width, size.height );
41273 // this is for the account management tab -seems to work there.
41274 var thd = grid.getGridEl().select('thead',true).first();
41276 // tbd.setSize(size.width, size.height - thd.getHeight());
41285 beforeSlide : function(){
41286 this.grid.getView().scroller.clip();
41289 afterSlide : function(){
41290 this.grid.getView().scroller.unclip();
41293 destroy : function(){
41294 this.grid.destroy();
41296 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41301 * @class Roo.bootstrap.panel.Nest
41302 * @extends Roo.bootstrap.panel.Content
41304 * Create a new Panel, that can contain a layout.Border.
41307 * @param {Roo.BorderLayout} layout The layout for this panel
41308 * @param {String/Object} config A string to set only the title or a config object
41310 Roo.bootstrap.panel.Nest = function(config)
41312 // construct with only one argument..
41313 /* FIXME - implement nicer consturctors
41314 if (layout.layout) {
41316 layout = config.layout;
41317 delete config.layout;
41319 if (layout.xtype && !layout.getEl) {
41320 // then layout needs constructing..
41321 layout = Roo.factory(layout, Roo);
41325 config.el = config.layout.getEl();
41327 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41329 config.layout.monitorWindowResize = false; // turn off autosizing
41330 this.layout = config.layout;
41331 this.layout.getEl().addClass("roo-layout-nested-layout");
41332 this.layout.parent = this;
41339 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41341 setSize : function(width, height){
41342 if(!this.ignoreResize(width, height)){
41343 var size = this.adjustForComponents(width, height);
41344 var el = this.layout.getEl();
41345 if (size.height < 1) {
41346 el.setWidth(size.width);
41348 el.setSize(size.width, size.height);
41350 var touch = el.dom.offsetWidth;
41351 this.layout.layout();
41352 // ie requires a double layout on the first pass
41353 if(Roo.isIE && !this.initialized){
41354 this.initialized = true;
41355 this.layout.layout();
41360 // activate all subpanels if not currently active..
41362 setActiveState : function(active){
41363 this.active = active;
41364 this.setActiveClass(active);
41367 this.fireEvent("deactivate", this);
41371 this.fireEvent("activate", this);
41372 // not sure if this should happen before or after..
41373 if (!this.layout) {
41374 return; // should not happen..
41377 for (var r in this.layout.regions) {
41378 reg = this.layout.getRegion(r);
41379 if (reg.getActivePanel()) {
41380 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41381 reg.setActivePanel(reg.getActivePanel());
41384 if (!reg.panels.length) {
41387 reg.showPanel(reg.getPanel(0));
41396 * Returns the nested BorderLayout for this panel
41397 * @return {Roo.BorderLayout}
41399 getLayout : function(){
41400 return this.layout;
41404 * Adds a xtype elements to the layout of the nested panel
41408 xtype : 'ContentPanel',
41415 xtype : 'NestedLayoutPanel',
41421 items : [ ... list of content panels or nested layout panels.. ]
41425 * @param {Object} cfg Xtype definition of item to add.
41427 addxtype : function(cfg) {
41428 return this.layout.addxtype(cfg);
41433 * Ext JS Library 1.1.1
41434 * Copyright(c) 2006-2007, Ext JS, LLC.
41436 * Originally Released Under LGPL - original licence link has changed is not relivant.
41439 * <script type="text/javascript">
41442 * @class Roo.TabPanel
41443 * @extends Roo.util.Observable
41444 * A lightweight tab container.
41448 // basic tabs 1, built from existing content
41449 var tabs = new Roo.TabPanel("tabs1");
41450 tabs.addTab("script", "View Script");
41451 tabs.addTab("markup", "View Markup");
41452 tabs.activate("script");
41454 // more advanced tabs, built from javascript
41455 var jtabs = new Roo.TabPanel("jtabs");
41456 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41458 // set up the UpdateManager
41459 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41460 var updater = tab2.getUpdateManager();
41461 updater.setDefaultUrl("ajax1.htm");
41462 tab2.on('activate', updater.refresh, updater, true);
41464 // Use setUrl for Ajax loading
41465 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41466 tab3.setUrl("ajax2.htm", null, true);
41469 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41472 jtabs.activate("jtabs-1");
41475 * Create a new TabPanel.
41476 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41477 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41479 Roo.bootstrap.panel.Tabs = function(config){
41481 * The container element for this TabPanel.
41482 * @type Roo.Element
41484 this.el = Roo.get(config.el);
41487 if(typeof config == "boolean"){
41488 this.tabPosition = config ? "bottom" : "top";
41490 Roo.apply(this, config);
41494 if(this.tabPosition == "bottom"){
41495 // if tabs are at the bottom = create the body first.
41496 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41497 this.el.addClass("roo-tabs-bottom");
41499 // next create the tabs holders
41501 if (this.tabPosition == "west"){
41503 var reg = this.region; // fake it..
41505 if (!reg.mgr.parent) {
41508 reg = reg.mgr.parent.region;
41510 Roo.log("got nest?");
41512 if (reg.mgr.getRegion('west')) {
41513 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41514 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41515 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41516 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41517 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41525 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41526 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41527 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41528 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41533 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41536 // finally - if tabs are at the top, then create the body last..
41537 if(this.tabPosition != "bottom"){
41538 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41539 * @type Roo.Element
41541 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41542 this.el.addClass("roo-tabs-top");
41546 this.bodyEl.setStyle("position", "relative");
41548 this.active = null;
41549 this.activateDelegate = this.activate.createDelegate(this);
41554 * Fires when the active tab changes
41555 * @param {Roo.TabPanel} this
41556 * @param {Roo.TabPanelItem} activePanel The new active tab
41560 * @event beforetabchange
41561 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41562 * @param {Roo.TabPanel} this
41563 * @param {Object} e Set cancel to true on this object to cancel the tab change
41564 * @param {Roo.TabPanelItem} tab The tab being changed to
41566 "beforetabchange" : true
41569 Roo.EventManager.onWindowResize(this.onResize, this);
41570 this.cpad = this.el.getPadding("lr");
41571 this.hiddenCount = 0;
41574 // toolbar on the tabbar support...
41575 if (this.toolbar) {
41576 alert("no toolbar support yet");
41577 this.toolbar = false;
41579 var tcfg = this.toolbar;
41580 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41581 this.toolbar = new Roo.Toolbar(tcfg);
41582 if (Roo.isSafari) {
41583 var tbl = tcfg.container.child('table', true);
41584 tbl.setAttribute('width', '100%');
41592 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41595 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41597 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41599 tabPosition : "top",
41601 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41603 currentTabWidth : 0,
41605 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41609 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41613 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41615 preferredTabWidth : 175,
41617 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41619 resizeTabs : false,
41621 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41623 monitorResize : true,
41625 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41627 toolbar : false, // set by caller..
41629 region : false, /// set by caller
41631 disableTooltips : true, // not used yet...
41634 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41635 * @param {String} id The id of the div to use <b>or create</b>
41636 * @param {String} text The text for the tab
41637 * @param {String} content (optional) Content to put in the TabPanelItem body
41638 * @param {Boolean} closable (optional) True to create a close icon on the tab
41639 * @return {Roo.TabPanelItem} The created TabPanelItem
41641 addTab : function(id, text, content, closable, tpl)
41643 var item = new Roo.bootstrap.panel.TabItem({
41647 closable : closable,
41650 this.addTabItem(item);
41652 item.setContent(content);
41658 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41659 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41660 * @return {Roo.TabPanelItem}
41662 getTab : function(id){
41663 return this.items[id];
41667 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41668 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41670 hideTab : function(id){
41671 var t = this.items[id];
41674 this.hiddenCount++;
41675 this.autoSizeTabs();
41680 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41681 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41683 unhideTab : function(id){
41684 var t = this.items[id];
41686 t.setHidden(false);
41687 this.hiddenCount--;
41688 this.autoSizeTabs();
41693 * Adds an existing {@link Roo.TabPanelItem}.
41694 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41696 addTabItem : function(item)
41698 this.items[item.id] = item;
41699 this.items.push(item);
41700 this.autoSizeTabs();
41701 // if(this.resizeTabs){
41702 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41703 // this.autoSizeTabs();
41705 // item.autoSize();
41710 * Removes a {@link Roo.TabPanelItem}.
41711 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41713 removeTab : function(id){
41714 var items = this.items;
41715 var tab = items[id];
41716 if(!tab) { return; }
41717 var index = items.indexOf(tab);
41718 if(this.active == tab && items.length > 1){
41719 var newTab = this.getNextAvailable(index);
41724 this.stripEl.dom.removeChild(tab.pnode.dom);
41725 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41726 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41728 items.splice(index, 1);
41729 delete this.items[tab.id];
41730 tab.fireEvent("close", tab);
41731 tab.purgeListeners();
41732 this.autoSizeTabs();
41735 getNextAvailable : function(start){
41736 var items = this.items;
41738 // look for a next tab that will slide over to
41739 // replace the one being removed
41740 while(index < items.length){
41741 var item = items[++index];
41742 if(item && !item.isHidden()){
41746 // if one isn't found select the previous tab (on the left)
41749 var item = items[--index];
41750 if(item && !item.isHidden()){
41758 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41759 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41761 disableTab : function(id){
41762 var tab = this.items[id];
41763 if(tab && this.active != tab){
41769 * Enables a {@link Roo.TabPanelItem} that is disabled.
41770 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41772 enableTab : function(id){
41773 var tab = this.items[id];
41778 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41779 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41780 * @return {Roo.TabPanelItem} The TabPanelItem.
41782 activate : function(id)
41784 //Roo.log('activite:' + id);
41786 var tab = this.items[id];
41790 if(tab == this.active || tab.disabled){
41794 this.fireEvent("beforetabchange", this, e, tab);
41795 if(e.cancel !== true && !tab.disabled){
41797 this.active.hide();
41799 this.active = this.items[id];
41800 this.active.show();
41801 this.fireEvent("tabchange", this, this.active);
41807 * Gets the active {@link Roo.TabPanelItem}.
41808 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41810 getActiveTab : function(){
41811 return this.active;
41815 * Updates the tab body element to fit the height of the container element
41816 * for overflow scrolling
41817 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41819 syncHeight : function(targetHeight){
41820 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41821 var bm = this.bodyEl.getMargins();
41822 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41823 this.bodyEl.setHeight(newHeight);
41827 onResize : function(){
41828 if(this.monitorResize){
41829 this.autoSizeTabs();
41834 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41836 beginUpdate : function(){
41837 this.updating = true;
41841 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41843 endUpdate : function(){
41844 this.updating = false;
41845 this.autoSizeTabs();
41849 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41851 autoSizeTabs : function()
41853 var count = this.items.length;
41854 var vcount = count - this.hiddenCount;
41857 this.stripEl.hide();
41859 this.stripEl.show();
41862 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41867 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41868 var availWidth = Math.floor(w / vcount);
41869 var b = this.stripBody;
41870 if(b.getWidth() > w){
41871 var tabs = this.items;
41872 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41873 if(availWidth < this.minTabWidth){
41874 /*if(!this.sleft){ // incomplete scrolling code
41875 this.createScrollButtons();
41878 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41881 if(this.currentTabWidth < this.preferredTabWidth){
41882 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41888 * Returns the number of tabs in this TabPanel.
41891 getCount : function(){
41892 return this.items.length;
41896 * Resizes all the tabs to the passed width
41897 * @param {Number} The new width
41899 setTabWidth : function(width){
41900 this.currentTabWidth = width;
41901 for(var i = 0, len = this.items.length; i < len; i++) {
41902 if(!this.items[i].isHidden()) {
41903 this.items[i].setWidth(width);
41909 * Destroys this TabPanel
41910 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41912 destroy : function(removeEl){
41913 Roo.EventManager.removeResizeListener(this.onResize, this);
41914 for(var i = 0, len = this.items.length; i < len; i++){
41915 this.items[i].purgeListeners();
41917 if(removeEl === true){
41918 this.el.update("");
41923 createStrip : function(container)
41925 var strip = document.createElement("nav");
41926 strip.className = Roo.bootstrap.version == 4 ?
41927 "navbar-light bg-light" :
41928 "navbar navbar-default"; //"x-tabs-wrap";
41929 container.appendChild(strip);
41933 createStripList : function(strip)
41935 // div wrapper for retard IE
41936 // returns the "tr" element.
41937 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41938 //'<div class="x-tabs-strip-wrap">'+
41939 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41940 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41941 return strip.firstChild; //.firstChild.firstChild.firstChild;
41943 createBody : function(container)
41945 var body = document.createElement("div");
41946 Roo.id(body, "tab-body");
41947 //Roo.fly(body).addClass("x-tabs-body");
41948 Roo.fly(body).addClass("tab-content");
41949 container.appendChild(body);
41952 createItemBody :function(bodyEl, id){
41953 var body = Roo.getDom(id);
41955 body = document.createElement("div");
41958 //Roo.fly(body).addClass("x-tabs-item-body");
41959 Roo.fly(body).addClass("tab-pane");
41960 bodyEl.insertBefore(body, bodyEl.firstChild);
41964 createStripElements : function(stripEl, text, closable, tpl)
41966 var td = document.createElement("li"); // was td..
41967 td.className = 'nav-item';
41969 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41972 stripEl.appendChild(td);
41974 td.className = "x-tabs-closable";
41975 if(!this.closeTpl){
41976 this.closeTpl = new Roo.Template(
41977 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41978 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41979 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41982 var el = this.closeTpl.overwrite(td, {"text": text});
41983 var close = el.getElementsByTagName("div")[0];
41984 var inner = el.getElementsByTagName("em")[0];
41985 return {"el": el, "close": close, "inner": inner};
41988 // not sure what this is..
41989 // if(!this.tabTpl){
41990 //this.tabTpl = new Roo.Template(
41991 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41992 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41994 // this.tabTpl = new Roo.Template(
41995 // '<a href="#">' +
41996 // '<span unselectable="on"' +
41997 // (this.disableTooltips ? '' : ' title="{text}"') +
41998 // ' >{text}</span></a>'
42004 var template = tpl || this.tabTpl || false;
42007 template = new Roo.Template(
42008 Roo.bootstrap.version == 4 ?
42010 '<a class="nav-link" href="#" unselectable="on"' +
42011 (this.disableTooltips ? '' : ' title="{text}"') +
42014 '<a class="nav-link" href="#">' +
42015 '<span unselectable="on"' +
42016 (this.disableTooltips ? '' : ' title="{text}"') +
42017 ' >{text}</span></a>'
42022 switch (typeof(template)) {
42026 template = new Roo.Template(template);
42032 var el = template.overwrite(td, {"text": text});
42034 var inner = el.getElementsByTagName("span")[0];
42036 return {"el": el, "inner": inner};
42044 * @class Roo.TabPanelItem
42045 * @extends Roo.util.Observable
42046 * Represents an individual item (tab plus body) in a TabPanel.
42047 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42048 * @param {String} id The id of this TabPanelItem
42049 * @param {String} text The text for the tab of this TabPanelItem
42050 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42052 Roo.bootstrap.panel.TabItem = function(config){
42054 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42055 * @type Roo.TabPanel
42057 this.tabPanel = config.panel;
42059 * The id for this TabPanelItem
42062 this.id = config.id;
42064 this.disabled = false;
42066 this.text = config.text;
42068 this.loaded = false;
42069 this.closable = config.closable;
42072 * The body element for this TabPanelItem.
42073 * @type Roo.Element
42075 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42076 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42077 this.bodyEl.setStyle("display", "block");
42078 this.bodyEl.setStyle("zoom", "1");
42079 //this.hideAction();
42081 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42083 this.el = Roo.get(els.el);
42084 this.inner = Roo.get(els.inner, true);
42085 this.textEl = Roo.bootstrap.version == 4 ?
42086 this.el : Roo.get(this.el.dom.firstChild, true);
42088 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42089 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42092 // this.el.on("mousedown", this.onTabMouseDown, this);
42093 this.el.on("click", this.onTabClick, this);
42095 if(config.closable){
42096 var c = Roo.get(els.close, true);
42097 c.dom.title = this.closeText;
42098 c.addClassOnOver("close-over");
42099 c.on("click", this.closeClick, this);
42105 * Fires when this tab becomes the active tab.
42106 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42107 * @param {Roo.TabPanelItem} this
42111 * @event beforeclose
42112 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42113 * @param {Roo.TabPanelItem} this
42114 * @param {Object} e Set cancel to true on this object to cancel the close.
42116 "beforeclose": true,
42119 * Fires when this tab is closed.
42120 * @param {Roo.TabPanelItem} this
42124 * @event deactivate
42125 * Fires when this tab is no longer the active tab.
42126 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42127 * @param {Roo.TabPanelItem} this
42129 "deactivate" : true
42131 this.hidden = false;
42133 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42136 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42138 purgeListeners : function(){
42139 Roo.util.Observable.prototype.purgeListeners.call(this);
42140 this.el.removeAllListeners();
42143 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42146 this.status_node.addClass("active");
42149 this.tabPanel.stripWrap.repaint();
42151 this.fireEvent("activate", this.tabPanel, this);
42155 * Returns true if this tab is the active tab.
42156 * @return {Boolean}
42158 isActive : function(){
42159 return this.tabPanel.getActiveTab() == this;
42163 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42166 this.status_node.removeClass("active");
42168 this.fireEvent("deactivate", this.tabPanel, this);
42171 hideAction : function(){
42172 this.bodyEl.hide();
42173 this.bodyEl.setStyle("position", "absolute");
42174 this.bodyEl.setLeft("-20000px");
42175 this.bodyEl.setTop("-20000px");
42178 showAction : function(){
42179 this.bodyEl.setStyle("position", "relative");
42180 this.bodyEl.setTop("");
42181 this.bodyEl.setLeft("");
42182 this.bodyEl.show();
42186 * Set the tooltip for the tab.
42187 * @param {String} tooltip The tab's tooltip
42189 setTooltip : function(text){
42190 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42191 this.textEl.dom.qtip = text;
42192 this.textEl.dom.removeAttribute('title');
42194 this.textEl.dom.title = text;
42198 onTabClick : function(e){
42199 e.preventDefault();
42200 this.tabPanel.activate(this.id);
42203 onTabMouseDown : function(e){
42204 e.preventDefault();
42205 this.tabPanel.activate(this.id);
42208 getWidth : function(){
42209 return this.inner.getWidth();
42212 setWidth : function(width){
42213 var iwidth = width - this.linode.getPadding("lr");
42214 this.inner.setWidth(iwidth);
42215 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42216 this.linode.setWidth(width);
42220 * Show or hide the tab
42221 * @param {Boolean} hidden True to hide or false to show.
42223 setHidden : function(hidden){
42224 this.hidden = hidden;
42225 this.linode.setStyle("display", hidden ? "none" : "");
42229 * Returns true if this tab is "hidden"
42230 * @return {Boolean}
42232 isHidden : function(){
42233 return this.hidden;
42237 * Returns the text for this tab
42240 getText : function(){
42244 autoSize : function(){
42245 //this.el.beginMeasure();
42246 this.textEl.setWidth(1);
42248 * #2804 [new] Tabs in Roojs
42249 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42251 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42252 //this.el.endMeasure();
42256 * Sets the text for the tab (Note: this also sets the tooltip text)
42257 * @param {String} text The tab's text and tooltip
42259 setText : function(text){
42261 this.textEl.update(text);
42262 this.setTooltip(text);
42263 //if(!this.tabPanel.resizeTabs){
42264 // this.autoSize();
42268 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42270 activate : function(){
42271 this.tabPanel.activate(this.id);
42275 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42277 disable : function(){
42278 if(this.tabPanel.active != this){
42279 this.disabled = true;
42280 this.status_node.addClass("disabled");
42285 * Enables this TabPanelItem if it was previously disabled.
42287 enable : function(){
42288 this.disabled = false;
42289 this.status_node.removeClass("disabled");
42293 * Sets the content for this TabPanelItem.
42294 * @param {String} content The content
42295 * @param {Boolean} loadScripts true to look for and load scripts
42297 setContent : function(content, loadScripts){
42298 this.bodyEl.update(content, loadScripts);
42302 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42303 * @return {Roo.UpdateManager} The UpdateManager
42305 getUpdateManager : function(){
42306 return this.bodyEl.getUpdateManager();
42310 * Set a URL to be used to load the content for this TabPanelItem.
42311 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42312 * @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)
42313 * @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)
42314 * @return {Roo.UpdateManager} The UpdateManager
42316 setUrl : function(url, params, loadOnce){
42317 if(this.refreshDelegate){
42318 this.un('activate', this.refreshDelegate);
42320 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42321 this.on("activate", this.refreshDelegate);
42322 return this.bodyEl.getUpdateManager();
42326 _handleRefresh : function(url, params, loadOnce){
42327 if(!loadOnce || !this.loaded){
42328 var updater = this.bodyEl.getUpdateManager();
42329 updater.update(url, params, this._setLoaded.createDelegate(this));
42334 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42335 * Will fail silently if the setUrl method has not been called.
42336 * This does not activate the panel, just updates its content.
42338 refresh : function(){
42339 if(this.refreshDelegate){
42340 this.loaded = false;
42341 this.refreshDelegate();
42346 _setLoaded : function(){
42347 this.loaded = true;
42351 closeClick : function(e){
42354 this.fireEvent("beforeclose", this, o);
42355 if(o.cancel !== true){
42356 this.tabPanel.removeTab(this.id);
42360 * The text displayed in the tooltip for the close icon.
42363 closeText : "Close this tab"
42366 * This script refer to:
42367 * Title: International Telephone Input
42368 * Author: Jack O'Connor
42369 * Code version: v12.1.12
42370 * Availability: https://github.com/jackocnr/intl-tel-input.git
42373 Roo.bootstrap.PhoneInputData = function() {
42376 "Afghanistan (افغانستان)",
42381 "Albania (Shqipëri)",
42386 "Algeria (الجزائر)",
42411 "Antigua and Barbuda",
42421 "Armenia (Հայաստան)",
42437 "Austria (Österreich)",
42442 "Azerbaijan (Azərbaycan)",
42452 "Bahrain (البحرين)",
42457 "Bangladesh (বাংলাদেশ)",
42467 "Belarus (Беларусь)",
42472 "Belgium (België)",
42502 "Bosnia and Herzegovina (Босна и Херцеговина)",
42517 "British Indian Ocean Territory",
42522 "British Virgin Islands",
42532 "Bulgaria (България)",
42542 "Burundi (Uburundi)",
42547 "Cambodia (កម្ពុជា)",
42552 "Cameroon (Cameroun)",
42561 ["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"]
42564 "Cape Verde (Kabu Verdi)",
42569 "Caribbean Netherlands",
42580 "Central African Republic (République centrafricaine)",
42600 "Christmas Island",
42606 "Cocos (Keeling) Islands",
42617 "Comoros (جزر القمر)",
42622 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42627 "Congo (Republic) (Congo-Brazzaville)",
42647 "Croatia (Hrvatska)",
42668 "Czech Republic (Česká republika)",
42673 "Denmark (Danmark)",
42688 "Dominican Republic (República Dominicana)",
42692 ["809", "829", "849"]
42710 "Equatorial Guinea (Guinea Ecuatorial)",
42730 "Falkland Islands (Islas Malvinas)",
42735 "Faroe Islands (Føroyar)",
42756 "French Guiana (Guyane française)",
42761 "French Polynesia (Polynésie française)",
42776 "Georgia (საქართველო)",
42781 "Germany (Deutschland)",
42801 "Greenland (Kalaallit Nunaat)",
42838 "Guinea-Bissau (Guiné Bissau)",
42863 "Hungary (Magyarország)",
42868 "Iceland (Ísland)",
42888 "Iraq (العراق)",
42904 "Israel (ישראל)",
42931 "Jordan (الأردن)",
42936 "Kazakhstan (Казахстан)",
42957 "Kuwait (الكويت)",
42962 "Kyrgyzstan (Кыргызстан)",
42972 "Latvia (Latvija)",
42977 "Lebanon (لبنان)",
42992 "Libya (ليبيا)",
43002 "Lithuania (Lietuva)",
43017 "Macedonia (FYROM) (Македонија)",
43022 "Madagascar (Madagasikara)",
43052 "Marshall Islands",
43062 "Mauritania (موريتانيا)",
43067 "Mauritius (Moris)",
43088 "Moldova (Republica Moldova)",
43098 "Mongolia (Монгол)",
43103 "Montenegro (Crna Gora)",
43113 "Morocco (المغرب)",
43119 "Mozambique (Moçambique)",
43124 "Myanmar (Burma) (မြန်မာ)",
43129 "Namibia (Namibië)",
43144 "Netherlands (Nederland)",
43149 "New Caledonia (Nouvelle-Calédonie)",
43184 "North Korea (조선 민주주의 인민 공화국)",
43189 "Northern Mariana Islands",
43205 "Pakistan (پاکستان)",
43215 "Palestine (فلسطين)",
43225 "Papua New Guinea",
43267 "Réunion (La Réunion)",
43273 "Romania (România)",
43289 "Saint Barthélemy",
43300 "Saint Kitts and Nevis",
43310 "Saint Martin (Saint-Martin (partie française))",
43316 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43321 "Saint Vincent and the Grenadines",
43336 "São Tomé and Príncipe (São Tomé e Príncipe)",
43341 "Saudi Arabia (المملكة العربية السعودية)",
43346 "Senegal (Sénégal)",
43376 "Slovakia (Slovensko)",
43381 "Slovenia (Slovenija)",
43391 "Somalia (Soomaaliya)",
43401 "South Korea (대한민국)",
43406 "South Sudan (جنوب السودان)",
43416 "Sri Lanka (ශ්රී ලංකාව)",
43421 "Sudan (السودان)",
43431 "Svalbard and Jan Mayen",
43442 "Sweden (Sverige)",
43447 "Switzerland (Schweiz)",
43452 "Syria (سوريا)",
43497 "Trinidad and Tobago",
43502 "Tunisia (تونس)",
43507 "Turkey (Türkiye)",
43517 "Turks and Caicos Islands",
43527 "U.S. Virgin Islands",
43537 "Ukraine (Україна)",
43542 "United Arab Emirates (الإمارات العربية المتحدة)",
43564 "Uzbekistan (Oʻzbekiston)",
43574 "Vatican City (Città del Vaticano)",
43585 "Vietnam (Việt Nam)",
43590 "Wallis and Futuna (Wallis-et-Futuna)",
43595 "Western Sahara (الصحراء الغربية)",
43601 "Yemen (اليمن)",
43625 * This script refer to:
43626 * Title: International Telephone Input
43627 * Author: Jack O'Connor
43628 * Code version: v12.1.12
43629 * Availability: https://github.com/jackocnr/intl-tel-input.git
43633 * @class Roo.bootstrap.PhoneInput
43634 * @extends Roo.bootstrap.TriggerField
43635 * An input with International dial-code selection
43637 * @cfg {String} defaultDialCode default '+852'
43638 * @cfg {Array} preferedCountries default []
43641 * Create a new PhoneInput.
43642 * @param {Object} config Configuration options
43645 Roo.bootstrap.PhoneInput = function(config) {
43646 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43649 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43651 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43653 listWidth: undefined,
43655 selectedClass: 'active',
43657 invalidClass : "has-warning",
43659 validClass: 'has-success',
43661 allowed: '0123456789',
43666 * @cfg {String} defaultDialCode The default dial code when initializing the input
43668 defaultDialCode: '+852',
43671 * @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
43673 preferedCountries: false,
43675 getAutoCreate : function()
43677 var data = Roo.bootstrap.PhoneInputData();
43678 var align = this.labelAlign || this.parentLabelAlign();
43681 this.allCountries = [];
43682 this.dialCodeMapping = [];
43684 for (var i = 0; i < data.length; i++) {
43686 this.allCountries[i] = {
43690 priority: c[3] || 0,
43691 areaCodes: c[4] || null
43693 this.dialCodeMapping[c[2]] = {
43696 priority: c[3] || 0,
43697 areaCodes: c[4] || null
43709 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43710 maxlength: this.max_length,
43711 cls : 'form-control tel-input',
43712 autocomplete: 'new-password'
43715 var hiddenInput = {
43718 cls: 'hidden-tel-input'
43722 hiddenInput.name = this.name;
43725 if (this.disabled) {
43726 input.disabled = true;
43729 var flag_container = {
43746 cls: this.hasFeedback ? 'has-feedback' : '',
43752 cls: 'dial-code-holder',
43759 cls: 'roo-select2-container input-group',
43766 if (this.fieldLabel.length) {
43769 tooltip: 'This field is required'
43775 cls: 'control-label',
43781 html: this.fieldLabel
43784 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43790 if(this.indicatorpos == 'right') {
43791 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43798 if(align == 'left') {
43806 if(this.labelWidth > 12){
43807 label.style = "width: " + this.labelWidth + 'px';
43809 if(this.labelWidth < 13 && this.labelmd == 0){
43810 this.labelmd = this.labelWidth;
43812 if(this.labellg > 0){
43813 label.cls += ' col-lg-' + this.labellg;
43814 input.cls += ' col-lg-' + (12 - this.labellg);
43816 if(this.labelmd > 0){
43817 label.cls += ' col-md-' + this.labelmd;
43818 container.cls += ' col-md-' + (12 - this.labelmd);
43820 if(this.labelsm > 0){
43821 label.cls += ' col-sm-' + this.labelsm;
43822 container.cls += ' col-sm-' + (12 - this.labelsm);
43824 if(this.labelxs > 0){
43825 label.cls += ' col-xs-' + this.labelxs;
43826 container.cls += ' col-xs-' + (12 - this.labelxs);
43836 var settings = this;
43838 ['xs','sm','md','lg'].map(function(size){
43839 if (settings[size]) {
43840 cfg.cls += ' col-' + size + '-' + settings[size];
43844 this.store = new Roo.data.Store({
43845 proxy : new Roo.data.MemoryProxy({}),
43846 reader : new Roo.data.JsonReader({
43857 'name' : 'dialCode',
43861 'name' : 'priority',
43865 'name' : 'areaCodes',
43872 if(!this.preferedCountries) {
43873 this.preferedCountries = [
43880 var p = this.preferedCountries.reverse();
43883 for (var i = 0; i < p.length; i++) {
43884 for (var j = 0; j < this.allCountries.length; j++) {
43885 if(this.allCountries[j].iso2 == p[i]) {
43886 var t = this.allCountries[j];
43887 this.allCountries.splice(j,1);
43888 this.allCountries.unshift(t);
43894 this.store.proxy.data = {
43896 data: this.allCountries
43902 initEvents : function()
43905 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43907 this.indicator = this.indicatorEl();
43908 this.flag = this.flagEl();
43909 this.dialCodeHolder = this.dialCodeHolderEl();
43911 this.trigger = this.el.select('div.flag-box',true).first();
43912 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43917 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43918 _this.list.setWidth(lw);
43921 this.list.on('mouseover', this.onViewOver, this);
43922 this.list.on('mousemove', this.onViewMove, this);
43923 this.inputEl().on("keyup", this.onKeyUp, this);
43924 this.inputEl().on("keypress", this.onKeyPress, this);
43926 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43928 this.view = new Roo.View(this.list, this.tpl, {
43929 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43932 this.view.on('click', this.onViewClick, this);
43933 this.setValue(this.defaultDialCode);
43936 onTriggerClick : function(e)
43938 Roo.log('trigger click');
43943 if(this.isExpanded()){
43945 this.hasFocus = false;
43947 this.store.load({});
43948 this.hasFocus = true;
43953 isExpanded : function()
43955 return this.list.isVisible();
43958 collapse : function()
43960 if(!this.isExpanded()){
43964 Roo.get(document).un('mousedown', this.collapseIf, this);
43965 Roo.get(document).un('mousewheel', this.collapseIf, this);
43966 this.fireEvent('collapse', this);
43970 expand : function()
43974 if(this.isExpanded() || !this.hasFocus){
43978 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43979 this.list.setWidth(lw);
43982 this.restrictHeight();
43984 Roo.get(document).on('mousedown', this.collapseIf, this);
43985 Roo.get(document).on('mousewheel', this.collapseIf, this);
43987 this.fireEvent('expand', this);
43990 restrictHeight : function()
43992 this.list.alignTo(this.inputEl(), this.listAlign);
43993 this.list.alignTo(this.inputEl(), this.listAlign);
43996 onViewOver : function(e, t)
43998 if(this.inKeyMode){
44001 var item = this.view.findItemFromChild(t);
44004 var index = this.view.indexOf(item);
44005 this.select(index, false);
44010 onViewClick : function(view, doFocus, el, e)
44012 var index = this.view.getSelectedIndexes()[0];
44014 var r = this.store.getAt(index);
44017 this.onSelect(r, index);
44019 if(doFocus !== false && !this.blockFocus){
44020 this.inputEl().focus();
44024 onViewMove : function(e, t)
44026 this.inKeyMode = false;
44029 select : function(index, scrollIntoView)
44031 this.selectedIndex = index;
44032 this.view.select(index);
44033 if(scrollIntoView !== false){
44034 var el = this.view.getNode(index);
44036 this.list.scrollChildIntoView(el, false);
44041 createList : function()
44043 this.list = Roo.get(document.body).createChild({
44045 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44046 style: 'display:none'
44049 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44052 collapseIf : function(e)
44054 var in_combo = e.within(this.el);
44055 var in_list = e.within(this.list);
44056 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44058 if (in_combo || in_list || is_list) {
44064 onSelect : function(record, index)
44066 if(this.fireEvent('beforeselect', this, record, index) !== false){
44068 this.setFlagClass(record.data.iso2);
44069 this.setDialCode(record.data.dialCode);
44070 this.hasFocus = false;
44072 this.fireEvent('select', this, record, index);
44076 flagEl : function()
44078 var flag = this.el.select('div.flag',true).first();
44085 dialCodeHolderEl : function()
44087 var d = this.el.select('input.dial-code-holder',true).first();
44094 setDialCode : function(v)
44096 this.dialCodeHolder.dom.value = '+'+v;
44099 setFlagClass : function(n)
44101 this.flag.dom.className = 'flag '+n;
44104 getValue : function()
44106 var v = this.inputEl().getValue();
44107 if(this.dialCodeHolder) {
44108 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44113 setValue : function(v)
44115 var d = this.getDialCode(v);
44117 //invalid dial code
44118 if(v.length == 0 || !d || d.length == 0) {
44120 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44121 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44127 this.setFlagClass(this.dialCodeMapping[d].iso2);
44128 this.setDialCode(d);
44129 this.inputEl().dom.value = v.replace('+'+d,'');
44130 this.hiddenEl().dom.value = this.getValue();
44135 getDialCode : function(v)
44139 if (v.length == 0) {
44140 return this.dialCodeHolder.dom.value;
44144 if (v.charAt(0) != "+") {
44147 var numericChars = "";
44148 for (var i = 1; i < v.length; i++) {
44149 var c = v.charAt(i);
44152 if (this.dialCodeMapping[numericChars]) {
44153 dialCode = v.substr(1, i);
44155 if (numericChars.length == 4) {
44165 this.setValue(this.defaultDialCode);
44169 hiddenEl : function()
44171 return this.el.select('input.hidden-tel-input',true).first();
44174 // after setting val
44175 onKeyUp : function(e){
44176 this.setValue(this.getValue());
44179 onKeyPress : function(e){
44180 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44187 * @class Roo.bootstrap.MoneyField
44188 * @extends Roo.bootstrap.ComboBox
44189 * Bootstrap MoneyField class
44192 * Create a new MoneyField.
44193 * @param {Object} config Configuration options
44196 Roo.bootstrap.MoneyField = function(config) {
44198 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44202 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44205 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44207 allowDecimals : true,
44209 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44211 decimalSeparator : ".",
44213 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44215 decimalPrecision : 0,
44217 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44219 allowNegative : true,
44221 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44225 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44227 minValue : Number.NEGATIVE_INFINITY,
44229 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44231 maxValue : Number.MAX_VALUE,
44233 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44235 minText : "The minimum value for this field is {0}",
44237 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44239 maxText : "The maximum value for this field is {0}",
44241 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44242 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44244 nanText : "{0} is not a valid number",
44246 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44250 * @cfg {String} defaults currency of the MoneyField
44251 * value should be in lkey
44253 defaultCurrency : false,
44255 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44257 thousandsDelimiter : false,
44259 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44270 getAutoCreate : function()
44272 var align = this.labelAlign || this.parentLabelAlign();
44284 cls : 'form-control roo-money-amount-input',
44285 autocomplete: 'new-password'
44288 var hiddenInput = {
44292 cls: 'hidden-number-input'
44295 if(this.max_length) {
44296 input.maxlength = this.max_length;
44300 hiddenInput.name = this.name;
44303 if (this.disabled) {
44304 input.disabled = true;
44307 var clg = 12 - this.inputlg;
44308 var cmd = 12 - this.inputmd;
44309 var csm = 12 - this.inputsm;
44310 var cxs = 12 - this.inputxs;
44314 cls : 'row roo-money-field',
44318 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44322 cls: 'roo-select2-container input-group',
44326 cls : 'form-control roo-money-currency-input',
44327 autocomplete: 'new-password',
44329 name : this.currencyName
44333 cls : 'input-group-addon',
44347 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44351 cls: this.hasFeedback ? 'has-feedback' : '',
44362 if (this.fieldLabel.length) {
44365 tooltip: 'This field is required'
44371 cls: 'control-label',
44377 html: this.fieldLabel
44380 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44386 if(this.indicatorpos == 'right') {
44387 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44394 if(align == 'left') {
44402 if(this.labelWidth > 12){
44403 label.style = "width: " + this.labelWidth + 'px';
44405 if(this.labelWidth < 13 && this.labelmd == 0){
44406 this.labelmd = this.labelWidth;
44408 if(this.labellg > 0){
44409 label.cls += ' col-lg-' + this.labellg;
44410 input.cls += ' col-lg-' + (12 - this.labellg);
44412 if(this.labelmd > 0){
44413 label.cls += ' col-md-' + this.labelmd;
44414 container.cls += ' col-md-' + (12 - this.labelmd);
44416 if(this.labelsm > 0){
44417 label.cls += ' col-sm-' + this.labelsm;
44418 container.cls += ' col-sm-' + (12 - this.labelsm);
44420 if(this.labelxs > 0){
44421 label.cls += ' col-xs-' + this.labelxs;
44422 container.cls += ' col-xs-' + (12 - this.labelxs);
44433 var settings = this;
44435 ['xs','sm','md','lg'].map(function(size){
44436 if (settings[size]) {
44437 cfg.cls += ' col-' + size + '-' + settings[size];
44444 initEvents : function()
44446 this.indicator = this.indicatorEl();
44448 this.initCurrencyEvent();
44450 this.initNumberEvent();
44453 initCurrencyEvent : function()
44456 throw "can not find store for combo";
44459 this.store = Roo.factory(this.store, Roo.data);
44460 this.store.parent = this;
44464 this.triggerEl = this.el.select('.input-group-addon', true).first();
44466 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44471 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44472 _this.list.setWidth(lw);
44475 this.list.on('mouseover', this.onViewOver, this);
44476 this.list.on('mousemove', this.onViewMove, this);
44477 this.list.on('scroll', this.onViewScroll, this);
44480 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44483 this.view = new Roo.View(this.list, this.tpl, {
44484 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44487 this.view.on('click', this.onViewClick, this);
44489 this.store.on('beforeload', this.onBeforeLoad, this);
44490 this.store.on('load', this.onLoad, this);
44491 this.store.on('loadexception', this.onLoadException, this);
44493 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44494 "up" : function(e){
44495 this.inKeyMode = true;
44499 "down" : function(e){
44500 if(!this.isExpanded()){
44501 this.onTriggerClick();
44503 this.inKeyMode = true;
44508 "enter" : function(e){
44511 if(this.fireEvent("specialkey", this, e)){
44512 this.onViewClick(false);
44518 "esc" : function(e){
44522 "tab" : function(e){
44525 if(this.fireEvent("specialkey", this, e)){
44526 this.onViewClick(false);
44534 doRelay : function(foo, bar, hname){
44535 if(hname == 'down' || this.scope.isExpanded()){
44536 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44544 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44548 initNumberEvent : function(e)
44550 this.inputEl().on("keydown" , this.fireKey, this);
44551 this.inputEl().on("focus", this.onFocus, this);
44552 this.inputEl().on("blur", this.onBlur, this);
44554 this.inputEl().relayEvent('keyup', this);
44556 if(this.indicator){
44557 this.indicator.addClass('invisible');
44560 this.originalValue = this.getValue();
44562 if(this.validationEvent == 'keyup'){
44563 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44564 this.inputEl().on('keyup', this.filterValidation, this);
44566 else if(this.validationEvent !== false){
44567 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44570 if(this.selectOnFocus){
44571 this.on("focus", this.preFocus, this);
44574 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44575 this.inputEl().on("keypress", this.filterKeys, this);
44577 this.inputEl().relayEvent('keypress', this);
44580 var allowed = "0123456789";
44582 if(this.allowDecimals){
44583 allowed += this.decimalSeparator;
44586 if(this.allowNegative){
44590 if(this.thousandsDelimiter) {
44594 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44596 var keyPress = function(e){
44598 var k = e.getKey();
44600 var c = e.getCharCode();
44603 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44604 allowed.indexOf(String.fromCharCode(c)) === -1
44610 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44614 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44619 this.inputEl().on("keypress", keyPress, this);
44623 onTriggerClick : function(e)
44630 this.loadNext = false;
44632 if(this.isExpanded()){
44637 this.hasFocus = true;
44639 if(this.triggerAction == 'all') {
44640 this.doQuery(this.allQuery, true);
44644 this.doQuery(this.getRawValue());
44647 getCurrency : function()
44649 var v = this.currencyEl().getValue();
44654 restrictHeight : function()
44656 this.list.alignTo(this.currencyEl(), this.listAlign);
44657 this.list.alignTo(this.currencyEl(), this.listAlign);
44660 onViewClick : function(view, doFocus, el, e)
44662 var index = this.view.getSelectedIndexes()[0];
44664 var r = this.store.getAt(index);
44667 this.onSelect(r, index);
44671 onSelect : function(record, index){
44673 if(this.fireEvent('beforeselect', this, record, index) !== false){
44675 this.setFromCurrencyData(index > -1 ? record.data : false);
44679 this.fireEvent('select', this, record, index);
44683 setFromCurrencyData : function(o)
44687 this.lastCurrency = o;
44689 if (this.currencyField) {
44690 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44692 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44695 this.lastSelectionText = currency;
44697 //setting default currency
44698 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44699 this.setCurrency(this.defaultCurrency);
44703 this.setCurrency(currency);
44706 setFromData : function(o)
44710 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44712 this.setFromCurrencyData(c);
44717 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44719 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44722 this.setValue(value);
44726 setCurrency : function(v)
44728 this.currencyValue = v;
44731 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44736 setValue : function(v)
44738 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44744 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44746 this.inputEl().dom.value = (v == '') ? '' :
44747 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44749 if(!this.allowZero && v === '0') {
44750 this.hiddenEl().dom.value = '';
44751 this.inputEl().dom.value = '';
44758 getRawValue : function()
44760 var v = this.inputEl().getValue();
44765 getValue : function()
44767 return this.fixPrecision(this.parseValue(this.getRawValue()));
44770 parseValue : function(value)
44772 if(this.thousandsDelimiter) {
44774 r = new RegExp(",", "g");
44775 value = value.replace(r, "");
44778 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44779 return isNaN(value) ? '' : value;
44783 fixPrecision : function(value)
44785 if(this.thousandsDelimiter) {
44787 r = new RegExp(",", "g");
44788 value = value.replace(r, "");
44791 var nan = isNaN(value);
44793 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44794 return nan ? '' : value;
44796 return parseFloat(value).toFixed(this.decimalPrecision);
44799 decimalPrecisionFcn : function(v)
44801 return Math.floor(v);
44804 validateValue : function(value)
44806 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44810 var num = this.parseValue(value);
44813 this.markInvalid(String.format(this.nanText, value));
44817 if(num < this.minValue){
44818 this.markInvalid(String.format(this.minText, this.minValue));
44822 if(num > this.maxValue){
44823 this.markInvalid(String.format(this.maxText, this.maxValue));
44830 validate : function()
44832 if(this.disabled || this.allowBlank){
44837 var currency = this.getCurrency();
44839 if(this.validateValue(this.getRawValue()) && currency.length){
44844 this.markInvalid();
44848 getName: function()
44853 beforeBlur : function()
44859 var v = this.parseValue(this.getRawValue());
44866 onBlur : function()
44870 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44871 //this.el.removeClass(this.focusClass);
44874 this.hasFocus = false;
44876 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44880 var v = this.getValue();
44882 if(String(v) !== String(this.startValue)){
44883 this.fireEvent('change', this, v, this.startValue);
44886 this.fireEvent("blur", this);
44889 inputEl : function()
44891 return this.el.select('.roo-money-amount-input', true).first();
44894 currencyEl : function()
44896 return this.el.select('.roo-money-currency-input', true).first();
44899 hiddenEl : function()
44901 return this.el.select('input.hidden-number-input',true).first();
44905 * @class Roo.bootstrap.BezierSignature
44906 * @extends Roo.bootstrap.Component
44907 * Bootstrap BezierSignature class
44908 * This script refer to:
44909 * Title: Signature Pad
44911 * Availability: https://github.com/szimek/signature_pad
44914 * Create a new BezierSignature
44915 * @param {Object} config The config object
44918 Roo.bootstrap.BezierSignature = function(config){
44919 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44925 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44932 mouse_btn_down: true,
44935 * @cfg {int} canvas height
44937 canvas_height: '200px',
44940 * @cfg {float|function} Radius of a single dot.
44945 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44950 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44955 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44960 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44965 * @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.
44967 bg_color: 'rgba(0, 0, 0, 0)',
44970 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44972 dot_color: 'black',
44975 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44977 velocity_filter_weight: 0.7,
44980 * @cfg {function} Callback when stroke begin.
44985 * @cfg {function} Callback when stroke end.
44989 getAutoCreate : function()
44991 var cls = 'roo-signature column';
44994 cls += ' ' + this.cls;
45004 for(var i = 0; i < col_sizes.length; i++) {
45005 if(this[col_sizes[i]]) {
45006 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45016 cls: 'roo-signature-body',
45020 cls: 'roo-signature-body-canvas',
45021 height: this.canvas_height,
45022 width: this.canvas_width
45029 style: 'display: none'
45037 initEvents: function()
45039 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45041 var canvas = this.canvasEl();
45043 // mouse && touch event swapping...
45044 canvas.dom.style.touchAction = 'none';
45045 canvas.dom.style.msTouchAction = 'none';
45047 this.mouse_btn_down = false;
45048 canvas.on('mousedown', this._handleMouseDown, this);
45049 canvas.on('mousemove', this._handleMouseMove, this);
45050 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45052 if (window.PointerEvent) {
45053 canvas.on('pointerdown', this._handleMouseDown, this);
45054 canvas.on('pointermove', this._handleMouseMove, this);
45055 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45058 if ('ontouchstart' in window) {
45059 canvas.on('touchstart', this._handleTouchStart, this);
45060 canvas.on('touchmove', this._handleTouchMove, this);
45061 canvas.on('touchend', this._handleTouchEnd, this);
45064 Roo.EventManager.onWindowResize(this.resize, this, true);
45066 // file input event
45067 this.fileEl().on('change', this.uploadImage, this);
45074 resize: function(){
45076 var canvas = this.canvasEl().dom;
45077 var ctx = this.canvasElCtx();
45078 var img_data = false;
45080 if(canvas.width > 0) {
45081 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45083 // setting canvas width will clean img data
45086 var style = window.getComputedStyle ?
45087 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45089 var padding_left = parseInt(style.paddingLeft) || 0;
45090 var padding_right = parseInt(style.paddingRight) || 0;
45092 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45095 ctx.putImageData(img_data, 0, 0);
45099 _handleMouseDown: function(e)
45101 if (e.browserEvent.which === 1) {
45102 this.mouse_btn_down = true;
45103 this.strokeBegin(e);
45107 _handleMouseMove: function (e)
45109 if (this.mouse_btn_down) {
45110 this.strokeMoveUpdate(e);
45114 _handleMouseUp: function (e)
45116 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45117 this.mouse_btn_down = false;
45122 _handleTouchStart: function (e) {
45124 e.preventDefault();
45125 if (e.browserEvent.targetTouches.length === 1) {
45126 // var touch = e.browserEvent.changedTouches[0];
45127 // this.strokeBegin(touch);
45129 this.strokeBegin(e); // assume e catching the correct xy...
45133 _handleTouchMove: function (e) {
45134 e.preventDefault();
45135 // var touch = event.targetTouches[0];
45136 // _this._strokeMoveUpdate(touch);
45137 this.strokeMoveUpdate(e);
45140 _handleTouchEnd: function (e) {
45141 var wasCanvasTouched = e.target === this.canvasEl().dom;
45142 if (wasCanvasTouched) {
45143 e.preventDefault();
45144 // var touch = event.changedTouches[0];
45145 // _this._strokeEnd(touch);
45150 reset: function () {
45151 this._lastPoints = [];
45152 this._lastVelocity = 0;
45153 this._lastWidth = (this.min_width + this.max_width) / 2;
45154 this.canvasElCtx().fillStyle = this.dot_color;
45157 strokeMoveUpdate: function(e)
45159 this.strokeUpdate(e);
45161 if (this.throttle) {
45162 this.throttleStroke(this.strokeUpdate, this.throttle);
45165 this.strokeUpdate(e);
45169 strokeBegin: function(e)
45171 var newPointGroup = {
45172 color: this.dot_color,
45176 if (typeof this.onBegin === 'function') {
45180 this.curve_data.push(newPointGroup);
45182 this.strokeUpdate(e);
45185 strokeUpdate: function(e)
45187 var rect = this.canvasEl().dom.getBoundingClientRect();
45188 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45189 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45190 var lastPoints = lastPointGroup.points;
45191 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45192 var isLastPointTooClose = lastPoint
45193 ? point.distanceTo(lastPoint) <= this.min_distance
45195 var color = lastPointGroup.color;
45196 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45197 var curve = this.addPoint(point);
45199 this.drawDot({color: color, point: point});
45202 this.drawCurve({color: color, curve: curve});
45212 strokeEnd: function(e)
45214 this.strokeUpdate(e);
45215 if (typeof this.onEnd === 'function') {
45220 addPoint: function (point) {
45221 var _lastPoints = this._lastPoints;
45222 _lastPoints.push(point);
45223 if (_lastPoints.length > 2) {
45224 if (_lastPoints.length === 3) {
45225 _lastPoints.unshift(_lastPoints[0]);
45227 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45228 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45229 _lastPoints.shift();
45235 calculateCurveWidths: function (startPoint, endPoint) {
45236 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45237 (1 - this.velocity_filter_weight) * this._lastVelocity;
45239 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45242 start: this._lastWidth
45245 this._lastVelocity = velocity;
45246 this._lastWidth = newWidth;
45250 drawDot: function (_a) {
45251 var color = _a.color, point = _a.point;
45252 var ctx = this.canvasElCtx();
45253 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45255 this.drawCurveSegment(point.x, point.y, width);
45257 ctx.fillStyle = color;
45261 drawCurve: function (_a) {
45262 var color = _a.color, curve = _a.curve;
45263 var ctx = this.canvasElCtx();
45264 var widthDelta = curve.endWidth - curve.startWidth;
45265 var drawSteps = Math.floor(curve.length()) * 2;
45267 ctx.fillStyle = color;
45268 for (var i = 0; i < drawSteps; i += 1) {
45269 var t = i / drawSteps;
45275 var x = uuu * curve.startPoint.x;
45276 x += 3 * uu * t * curve.control1.x;
45277 x += 3 * u * tt * curve.control2.x;
45278 x += ttt * curve.endPoint.x;
45279 var y = uuu * curve.startPoint.y;
45280 y += 3 * uu * t * curve.control1.y;
45281 y += 3 * u * tt * curve.control2.y;
45282 y += ttt * curve.endPoint.y;
45283 var width = curve.startWidth + ttt * widthDelta;
45284 this.drawCurveSegment(x, y, width);
45290 drawCurveSegment: function (x, y, width) {
45291 var ctx = this.canvasElCtx();
45293 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45294 this.is_empty = false;
45299 var ctx = this.canvasElCtx();
45300 var canvas = this.canvasEl().dom;
45301 ctx.fillStyle = this.bg_color;
45302 ctx.clearRect(0, 0, canvas.width, canvas.height);
45303 ctx.fillRect(0, 0, canvas.width, canvas.height);
45304 this.curve_data = [];
45306 this.is_empty = true;
45311 return this.el.select('input',true).first();
45314 canvasEl: function()
45316 return this.el.select('canvas',true).first();
45319 canvasElCtx: function()
45321 return this.el.select('canvas',true).first().dom.getContext('2d');
45324 getImage: function(type)
45326 if(this.is_empty) {
45331 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45334 drawFromImage: function(img_src)
45336 var img = new Image();
45338 img.onload = function(){
45339 this.canvasElCtx().drawImage(img, 0, 0);
45344 this.is_empty = false;
45347 selectImage: function()
45349 this.fileEl().dom.click();
45352 uploadImage: function(e)
45354 var reader = new FileReader();
45356 reader.onload = function(e){
45357 var img = new Image();
45358 img.onload = function(){
45360 this.canvasElCtx().drawImage(img, 0, 0);
45362 img.src = e.target.result;
45365 reader.readAsDataURL(e.target.files[0]);
45368 // Bezier Point Constructor
45369 Point: (function () {
45370 function Point(x, y, time) {
45373 this.time = time || Date.now();
45375 Point.prototype.distanceTo = function (start) {
45376 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45378 Point.prototype.equals = function (other) {
45379 return this.x === other.x && this.y === other.y && this.time === other.time;
45381 Point.prototype.velocityFrom = function (start) {
45382 return this.time !== start.time
45383 ? this.distanceTo(start) / (this.time - start.time)
45390 // Bezier Constructor
45391 Bezier: (function () {
45392 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45393 this.startPoint = startPoint;
45394 this.control2 = control2;
45395 this.control1 = control1;
45396 this.endPoint = endPoint;
45397 this.startWidth = startWidth;
45398 this.endWidth = endWidth;
45400 Bezier.fromPoints = function (points, widths, scope) {
45401 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45402 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45403 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45405 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45406 var dx1 = s1.x - s2.x;
45407 var dy1 = s1.y - s2.y;
45408 var dx2 = s2.x - s3.x;
45409 var dy2 = s2.y - s3.y;
45410 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45411 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45412 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45413 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45414 var dxm = m1.x - m2.x;
45415 var dym = m1.y - m2.y;
45416 var k = l2 / (l1 + l2);
45417 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45418 var tx = s2.x - cm.x;
45419 var ty = s2.y - cm.y;
45421 c1: new scope.Point(m1.x + tx, m1.y + ty),
45422 c2: new scope.Point(m2.x + tx, m2.y + ty)
45425 Bezier.prototype.length = function () {
45430 for (var i = 0; i <= steps; i += 1) {
45432 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45433 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45435 var xdiff = cx - px;
45436 var ydiff = cy - py;
45437 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45444 Bezier.prototype.point = function (t, start, c1, c2, end) {
45445 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45446 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45447 + (3.0 * c2 * (1.0 - t) * t * t)
45448 + (end * t * t * t);
45453 throttleStroke: function(fn, wait) {
45454 if (wait === void 0) { wait = 250; }
45456 var timeout = null;
45460 var later = function () {
45461 previous = Date.now();
45463 result = fn.apply(storedContext, storedArgs);
45465 storedContext = null;
45469 return function wrapper() {
45471 for (var _i = 0; _i < arguments.length; _i++) {
45472 args[_i] = arguments[_i];
45474 var now = Date.now();
45475 var remaining = wait - (now - previous);
45476 storedContext = this;
45478 if (remaining <= 0 || remaining > wait) {
45480 clearTimeout(timeout);
45484 result = fn.apply(storedContext, storedArgs);
45486 storedContext = null;
45490 else if (!timeout) {
45491 timeout = window.setTimeout(later, remaining);