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
4261 * Bootstrap Modal class
4262 * @cfg {String} title Title of dialog
4263 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4265 * @cfg {Boolean} specificTitle default false
4266 * @cfg {Array} buttons Array of buttons or standard button set..
4267 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268 * @cfg {Boolean} animate default true
4269 * @cfg {Boolean} allow_close default true
4270 * @cfg {Boolean} fitwindow default false
4271 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274 * @cfg {String} size (sm|lg|xl) default empty
4275 * @cfg {Number} max_width set the max width of modal
4276 * @cfg {Boolean} editableTitle can the title be edited
4281 * Create a new Modal Dialog
4282 * @param {Object} config The config object
4285 Roo.bootstrap.Modal = function(config){
4286 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291 * The raw btnclick event for the button
4292 * @param {Roo.EventObject} e
4297 * Fire when dialog resize
4298 * @param {Roo.bootstrap.Modal} this
4299 * @param {Roo.EventObject} e
4303 * @event titlechanged
4304 * Fire when the editable title has been changed
4305 * @param {Roo.bootstrap.Modal} this
4306 * @param {Roo.EventObject} value
4308 "titlechanged" : true
4311 this.buttons = this.buttons || [];
4314 this.tmpl = Roo.factory(this.tmpl);
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4321 title : 'test dialog',
4331 specificTitle: false,
4333 buttonPosition: 'right',
4355 editableTitle : false,
4357 onRender : function(ct, position)
4359 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362 var cfg = Roo.apply({}, this.getAutoCreate());
4365 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4367 //if (!cfg.name.length) {
4371 cfg.cls += ' ' + this.cls;
4374 cfg.style = this.style;
4376 this.el = Roo.get(document.body).createChild(cfg, position);
4378 //var type = this.el.dom.type;
4381 if(this.tabIndex !== undefined){
4382 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385 this.dialogEl = this.el.select('.modal-dialog',true).first();
4386 this.bodyEl = this.el.select('.modal-body',true).first();
4387 this.closeEl = this.el.select('.modal-header .close', true).first();
4388 this.headerEl = this.el.select('.modal-header',true).first();
4389 this.titleEl = this.el.select('.modal-title',true).first();
4390 this.footerEl = this.el.select('.modal-footer',true).first();
4392 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4394 //this.el.addClass("x-dlg-modal");
4396 if (this.buttons.length) {
4397 Roo.each(this.buttons, function(bb) {
4398 var b = Roo.apply({}, bb);
4399 b.xns = b.xns || Roo.bootstrap;
4400 b.xtype = b.xtype || 'Button';
4401 if (typeof(b.listeners) == 'undefined') {
4402 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4405 var btn = Roo.factory(b);
4407 btn.render(this.getButtonContainer());
4411 // render the children.
4414 if(typeof(this.items) != 'undefined'){
4415 var items = this.items;
4418 for(var i =0;i < items.length;i++) {
4419 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4423 this.items = nitems;
4425 // where are these used - they used to be body/close/footer
4429 //this.el.addClass([this.fieldClass, this.cls]);
4433 getAutoCreate : function()
4435 // we will default to modal-body-overflow - might need to remove or make optional later.
4437 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4438 html : this.html || ''
4443 cls : 'modal-title',
4447 if(this.specificTitle){ // WTF is this?
4452 if (this.allow_close && Roo.bootstrap.version == 3) {
4462 if (this.editableTitle) {
4464 cls: 'form-control roo-editable-title d-none',
4470 if (this.allow_close && Roo.bootstrap.version == 4) {
4480 if(this.size.length){
4481 size = 'modal-' + this.size;
4484 var footer = Roo.bootstrap.version == 3 ?
4486 cls : 'modal-footer',
4490 cls: 'btn-' + this.buttonPosition
4495 { // BS4 uses mr-auto on left buttons....
4496 cls : 'modal-footer'
4507 cls: "modal-dialog " + size,
4510 cls : "modal-content",
4513 cls : 'modal-header',
4528 modal.cls += ' fade';
4534 getChildContainer : function() {
4539 getButtonContainer : function() {
4541 return Roo.bootstrap.version == 4 ?
4542 this.el.select('.modal-footer',true).first()
4543 : this.el.select('.modal-footer div',true).first();
4546 initEvents : function()
4548 if (this.allow_close) {
4549 this.closeEl.on('click', this.hide, this);
4551 Roo.EventManager.onWindowResize(this.resize, this, true);
4552 if (this.editableTitle) {
4553 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4554 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4555 this.headerEditEl.on('keyup', function(e) {
4556 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4557 this.toggleHeaderInput(false)
4560 this.headerEditEl.on('blur', function(e) {
4561 this.toggleHeaderInput(false)
4570 this.maskEl.setSize(
4571 Roo.lib.Dom.getViewWidth(true),
4572 Roo.lib.Dom.getViewHeight(true)
4575 if (this.fitwindow) {
4577 this.dialogEl.setStyle( { 'max-width' : '100%' });
4579 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4580 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4585 if(this.max_width !== 0) {
4587 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590 this.setSize(w, this.height);
4594 if(this.max_height) {
4595 this.setSize(w,Math.min(
4597 Roo.lib.Dom.getViewportHeight(true) - 60
4603 if(!this.fit_content) {
4604 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4608 this.setSize(w, Math.min(
4610 this.headerEl.getHeight() +
4611 this.footerEl.getHeight() +
4612 this.getChildHeight(this.bodyEl.dom.childNodes),
4613 Roo.lib.Dom.getViewportHeight(true) - 60)
4619 setSize : function(w,h)
4630 if (!this.rendered) {
4633 this.toggleHeaderInput(false);
4634 //this.el.setStyle('display', 'block');
4635 this.el.removeClass('hideing');
4636 this.el.dom.style.display='block';
4638 Roo.get(document.body).addClass('modal-open');
4640 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4643 this.el.addClass('show');
4644 this.el.addClass('in');
4647 this.el.addClass('show');
4648 this.el.addClass('in');
4651 // not sure how we can show data in here..
4653 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4656 Roo.get(document.body).addClass("x-body-masked");
4658 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4659 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4660 this.maskEl.dom.style.display = 'block';
4661 this.maskEl.addClass('show');
4666 this.fireEvent('show', this);
4668 // set zindex here - otherwise it appears to be ignored...
4669 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672 this.items.forEach( function(e) {
4673 e.layout ? e.layout() : false;
4681 if(this.fireEvent("beforehide", this) !== false){
4683 this.maskEl.removeClass('show');
4685 this.maskEl.dom.style.display = '';
4686 Roo.get(document.body).removeClass("x-body-masked");
4687 this.el.removeClass('in');
4688 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4690 if(this.animate){ // why
4691 this.el.addClass('hideing');
4692 this.el.removeClass('show');
4694 if (!this.el.hasClass('hideing')) {
4695 return; // it's been shown again...
4698 this.el.dom.style.display='';
4700 Roo.get(document.body).removeClass('modal-open');
4701 this.el.removeClass('hideing');
4705 this.el.removeClass('show');
4706 this.el.dom.style.display='';
4707 Roo.get(document.body).removeClass('modal-open');
4710 this.fireEvent('hide', this);
4713 isVisible : function()
4716 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4720 addButton : function(str, cb)
4724 var b = Roo.apply({}, { html : str } );
4725 b.xns = b.xns || Roo.bootstrap;
4726 b.xtype = b.xtype || 'Button';
4727 if (typeof(b.listeners) == 'undefined') {
4728 b.listeners = { click : cb.createDelegate(this) };
4731 var btn = Roo.factory(b);
4733 btn.render(this.getButtonContainer());
4739 setDefaultButton : function(btn)
4741 //this.el.select('.modal-footer').()
4744 resizeTo: function(w,h)
4746 this.dialogEl.setWidth(w);
4748 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4750 this.bodyEl.setHeight(h - diff);
4752 this.fireEvent('resize', this);
4755 setContentSize : function(w, h)
4759 onButtonClick: function(btn,e)
4762 this.fireEvent('btnclick', btn.name, e);
4765 * Set the title of the Dialog
4766 * @param {String} str new Title
4768 setTitle: function(str) {
4769 this.titleEl.dom.innerHTML = str;
4773 * Set the body of the Dialog
4774 * @param {String} str new Title
4776 setBody: function(str) {
4777 this.bodyEl.dom.innerHTML = str;
4780 * Set the body of the Dialog using the template
4781 * @param {Obj} data - apply this data to the template and replace the body contents.
4783 applyBody: function(obj)
4786 Roo.log("Error - using apply Body without a template");
4789 this.tmpl.overwrite(this.bodyEl, obj);
4792 getChildHeight : function(child_nodes)
4796 child_nodes.length == 0
4801 var child_height = 0;
4803 for(var i = 0; i < child_nodes.length; i++) {
4806 * for modal with tabs...
4807 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4809 var layout_childs = child_nodes[i].childNodes;
4811 for(var j = 0; j < layout_childs.length; j++) {
4813 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4815 var layout_body_childs = layout_childs[j].childNodes;
4817 for(var k = 0; k < layout_body_childs.length; k++) {
4819 if(layout_body_childs[k].classList.contains('navbar')) {
4820 child_height += layout_body_childs[k].offsetHeight;
4824 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4826 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4828 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4830 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4831 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4846 child_height += child_nodes[i].offsetHeight;
4847 // Roo.log(child_nodes[i].offsetHeight);
4850 return child_height;
4852 toggleHeaderInput : function(is_edit)
4854 if (!this.editableTitle) {
4855 return; // not editable.
4857 if (is_edit && this.is_header_editing) {
4858 return; // already editing..
4862 this.headerEditEl.dom.value = this.title;
4863 this.headerEditEl.removeClass('d-none');
4864 this.headerEditEl.dom.focus();
4865 this.titleEl.addClass('d-none');
4867 this.is_header_editing = true;
4870 // flip back to not editing.
4871 this.title = this.headerEditEl.dom.value;
4872 this.headerEditEl.addClass('d-none');
4873 this.titleEl.removeClass('d-none');
4874 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4875 this.is_header_editing = false;
4876 this.fireEvent('titlechanged', this, this.title);
4885 Roo.apply(Roo.bootstrap.Modal, {
4887 * Button config that displays a single OK button
4896 * Button config that displays Yes and No buttons
4912 * Button config that displays OK and Cancel buttons
4927 * Button config that displays Yes, No and Cancel buttons
4952 * messagebox - can be used as a replace
4956 * @class Roo.MessageBox
4957 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4961 Roo.Msg.alert('Status', 'Changes saved successfully.');
4963 // Prompt for user data:
4964 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4966 // process text value...
4970 // Show a dialog using config options:
4972 title:'Save Changes?',
4973 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4974 buttons: Roo.Msg.YESNOCANCEL,
4981 Roo.bootstrap.MessageBox = function(){
4982 var dlg, opt, mask, waitTimer;
4983 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4984 var buttons, activeTextEl, bwidth;
4988 var handleButton = function(button){
4990 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4994 var handleHide = function(){
4996 dlg.el.removeClass(opt.cls);
4999 // Roo.TaskMgr.stop(waitTimer);
5000 // waitTimer = null;
5005 var updateButtons = function(b){
5008 buttons["ok"].hide();
5009 buttons["cancel"].hide();
5010 buttons["yes"].hide();
5011 buttons["no"].hide();
5012 dlg.footerEl.hide();
5016 dlg.footerEl.show();
5017 for(var k in buttons){
5018 if(typeof buttons[k] != "function"){
5021 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5022 width += buttons[k].el.getWidth()+15;
5032 var handleEsc = function(d, k, e){
5033 if(opt && opt.closable !== false){
5043 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5044 * @return {Roo.BasicDialog} The BasicDialog element
5046 getDialog : function(){
5048 dlg = new Roo.bootstrap.Modal( {
5051 //constraintoviewport:false,
5053 //collapsible : false,
5058 //buttonAlign:"center",
5059 closeClick : function(){
5060 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5063 handleButton("cancel");
5068 dlg.on("hide", handleHide);
5070 //dlg.addKeyListener(27, handleEsc);
5072 this.buttons = buttons;
5073 var bt = this.buttonText;
5074 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5075 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5076 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5077 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5079 bodyEl = dlg.bodyEl.createChild({
5081 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5082 '<textarea class="roo-mb-textarea"></textarea>' +
5083 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5085 msgEl = bodyEl.dom.firstChild;
5086 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5087 textboxEl.enableDisplayMode();
5088 textboxEl.addKeyListener([10,13], function(){
5089 if(dlg.isVisible() && opt && opt.buttons){
5092 }else if(opt.buttons.yes){
5093 handleButton("yes");
5097 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5098 textareaEl.enableDisplayMode();
5099 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5100 progressEl.enableDisplayMode();
5102 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5103 var pf = progressEl.dom.firstChild;
5105 pp = Roo.get(pf.firstChild);
5106 pp.setHeight(pf.offsetHeight);
5114 * Updates the message box body text
5115 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5116 * the XHTML-compliant non-breaking space character '&#160;')
5117 * @return {Roo.MessageBox} This message box
5119 updateText : function(text)
5121 if(!dlg.isVisible() && !opt.width){
5122 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5123 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5125 msgEl.innerHTML = text || ' ';
5127 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5128 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5130 Math.min(opt.width || cw , this.maxWidth),
5131 Math.max(opt.minWidth || this.minWidth, bwidth)
5134 activeTextEl.setWidth(w);
5136 if(dlg.isVisible()){
5137 dlg.fixedcenter = false;
5139 // to big, make it scroll. = But as usual stupid IE does not support
5142 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5143 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5144 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5146 bodyEl.dom.style.height = '';
5147 bodyEl.dom.style.overflowY = '';
5150 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5152 bodyEl.dom.style.overflowX = '';
5155 dlg.setContentSize(w, bodyEl.getHeight());
5156 if(dlg.isVisible()){
5157 dlg.fixedcenter = true;
5163 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5164 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5165 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5166 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5167 * @return {Roo.MessageBox} This message box
5169 updateProgress : function(value, text){
5171 this.updateText(text);
5174 if (pp) { // weird bug on my firefox - for some reason this is not defined
5175 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5176 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5182 * Returns true if the message box is currently displayed
5183 * @return {Boolean} True if the message box is visible, else false
5185 isVisible : function(){
5186 return dlg && dlg.isVisible();
5190 * Hides the message box if it is displayed
5193 if(this.isVisible()){
5199 * Displays a new message box, or reinitializes an existing message box, based on the config options
5200 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5201 * The following config object properties are supported:
5203 Property Type Description
5204 ---------- --------------- ------------------------------------------------------------------------------------
5205 animEl String/Element An id or Element from which the message box should animate as it opens and
5206 closes (defaults to undefined)
5207 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5208 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5209 closable Boolean False to hide the top-right close button (defaults to true). Note that
5210 progress and wait dialogs will ignore this property and always hide the
5211 close button as they can only be closed programmatically.
5212 cls String A custom CSS class to apply to the message box element
5213 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5214 displayed (defaults to 75)
5215 fn Function A callback function to execute after closing the dialog. The arguments to the
5216 function will be btn (the name of the button that was clicked, if applicable,
5217 e.g. "ok"), and text (the value of the active text field, if applicable).
5218 Progress and wait dialogs will ignore this option since they do not respond to
5219 user actions and can only be closed programmatically, so any required function
5220 should be called by the same code after it closes the dialog.
5221 icon String A CSS class that provides a background image to be used as an icon for
5222 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5223 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5224 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5225 modal Boolean False to allow user interaction with the page while the message box is
5226 displayed (defaults to true)
5227 msg String A string that will replace the existing message box body text (defaults
5228 to the XHTML-compliant non-breaking space character ' ')
5229 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5230 progress Boolean True to display a progress bar (defaults to false)
5231 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5232 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5233 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5234 title String The title text
5235 value String The string value to set into the active textbox element if displayed
5236 wait Boolean True to display a progress bar (defaults to false)
5237 width Number The width of the dialog in pixels
5244 msg: 'Please enter your address:',
5246 buttons: Roo.MessageBox.OKCANCEL,
5249 animEl: 'addAddressBtn'
5252 * @param {Object} config Configuration options
5253 * @return {Roo.MessageBox} This message box
5255 show : function(options)
5258 // this causes nightmares if you show one dialog after another
5259 // especially on callbacks..
5261 if(this.isVisible()){
5264 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5265 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5266 Roo.log("New Dialog Message:" + options.msg )
5267 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5268 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5271 var d = this.getDialog();
5273 d.setTitle(opt.title || " ");
5274 d.closeEl.setDisplayed(opt.closable !== false);
5275 activeTextEl = textboxEl;
5276 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5281 textareaEl.setHeight(typeof opt.multiline == "number" ?
5282 opt.multiline : this.defaultTextHeight);
5283 activeTextEl = textareaEl;
5292 progressEl.setDisplayed(opt.progress === true);
5294 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5296 this.updateProgress(0);
5297 activeTextEl.dom.value = opt.value || "";
5299 dlg.setDefaultButton(activeTextEl);
5301 var bs = opt.buttons;
5305 }else if(bs && bs.yes){
5306 db = buttons["yes"];
5308 dlg.setDefaultButton(db);
5310 bwidth = updateButtons(opt.buttons);
5311 this.updateText(opt.msg);
5313 d.el.addClass(opt.cls);
5315 d.proxyDrag = opt.proxyDrag === true;
5316 d.modal = opt.modal !== false;
5317 d.mask = opt.modal !== false ? mask : false;
5319 // force it to the end of the z-index stack so it gets a cursor in FF
5320 document.body.appendChild(dlg.el.dom);
5321 d.animateTarget = null;
5322 d.show(options.animEl);
5328 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5329 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5330 * and closing the message box when the process is complete.
5331 * @param {String} title The title bar text
5332 * @param {String} msg The message box body text
5333 * @return {Roo.MessageBox} This message box
5335 progress : function(title, msg){
5342 minWidth: this.minProgressWidth,
5349 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5350 * If a callback function is passed it will be called after the user clicks the button, and the
5351 * id of the button that was clicked will be passed as the only parameter to the callback
5352 * (could also be the top-right close button).
5353 * @param {String} title The title bar text
5354 * @param {String} msg The message box body text
5355 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5356 * @param {Object} scope (optional) The scope of the callback function
5357 * @return {Roo.MessageBox} This message box
5359 alert : function(title, msg, fn, scope)
5374 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5375 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5376 * You are responsible for closing the message box when the process is complete.
5377 * @param {String} msg The message box body text
5378 * @param {String} title (optional) The title bar text
5379 * @return {Roo.MessageBox} This message box
5381 wait : function(msg, title){
5392 waitTimer = Roo.TaskMgr.start({
5394 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5402 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5403 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5404 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5405 * @param {String} title The title bar text
5406 * @param {String} msg The message box body text
5407 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5408 * @param {Object} scope (optional) The scope of the callback function
5409 * @return {Roo.MessageBox} This message box
5411 confirm : function(title, msg, fn, scope){
5415 buttons: this.YESNO,
5424 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5425 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5426 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5427 * (could also be the top-right close button) and the text that was entered will be passed as the two
5428 * parameters to the callback.
5429 * @param {String} title The title bar text
5430 * @param {String} msg The message box body text
5431 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5432 * @param {Object} scope (optional) The scope of the callback function
5433 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5434 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5435 * @return {Roo.MessageBox} This message box
5437 prompt : function(title, msg, fn, scope, multiline){
5441 buttons: this.OKCANCEL,
5446 multiline: multiline,
5453 * Button config that displays a single OK button
5458 * Button config that displays Yes and No buttons
5461 YESNO : {yes:true, no:true},
5463 * Button config that displays OK and Cancel buttons
5466 OKCANCEL : {ok:true, cancel:true},
5468 * Button config that displays Yes, No and Cancel buttons
5471 YESNOCANCEL : {yes:true, no:true, cancel:true},
5474 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5477 defaultTextHeight : 75,
5479 * The maximum width in pixels of the message box (defaults to 600)
5484 * The minimum width in pixels of the message box (defaults to 100)
5489 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5490 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5493 minProgressWidth : 250,
5495 * An object containing the default button text strings that can be overriden for localized language support.
5496 * Supported properties are: ok, cancel, yes and no.
5497 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5510 * Shorthand for {@link Roo.MessageBox}
5512 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5513 Roo.Msg = Roo.Msg || Roo.MessageBox;
5522 * @class Roo.bootstrap.Navbar
5523 * @extends Roo.bootstrap.Component
5524 * Bootstrap Navbar class
5527 * Create a new Navbar
5528 * @param {Object} config The config object
5532 Roo.bootstrap.Navbar = function(config){
5533 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5537 * @event beforetoggle
5538 * Fire before toggle the menu
5539 * @param {Roo.EventObject} e
5541 "beforetoggle" : true
5545 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5554 getAutoCreate : function(){
5557 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5561 initEvents :function ()
5563 //Roo.log(this.el.select('.navbar-toggle',true));
5564 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5571 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5573 var size = this.el.getSize();
5574 this.maskEl.setSize(size.width, size.height);
5575 this.maskEl.enableDisplayMode("block");
5584 getChildContainer : function()
5586 if (this.el && this.el.select('.collapse').getCount()) {
5587 return this.el.select('.collapse',true).first();
5602 onToggle : function()
5605 if(this.fireEvent('beforetoggle', this) === false){
5608 var ce = this.el.select('.navbar-collapse',true).first();
5610 if (!ce.hasClass('show')) {
5620 * Expand the navbar pulldown
5622 expand : function ()
5625 var ce = this.el.select('.navbar-collapse',true).first();
5626 if (ce.hasClass('collapsing')) {
5629 ce.dom.style.height = '';
5631 ce.addClass('in'); // old...
5632 ce.removeClass('collapse');
5633 ce.addClass('show');
5634 var h = ce.getHeight();
5636 ce.removeClass('show');
5637 // at this point we should be able to see it..
5638 ce.addClass('collapsing');
5640 ce.setHeight(0); // resize it ...
5641 ce.on('transitionend', function() {
5642 //Roo.log('done transition');
5643 ce.removeClass('collapsing');
5644 ce.addClass('show');
5645 ce.removeClass('collapse');
5647 ce.dom.style.height = '';
5648 }, this, { single: true} );
5650 ce.dom.scrollTop = 0;
5653 * Collapse the navbar pulldown
5655 collapse : function()
5657 var ce = this.el.select('.navbar-collapse',true).first();
5659 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5660 // it's collapsed or collapsing..
5663 ce.removeClass('in'); // old...
5664 ce.setHeight(ce.getHeight());
5665 ce.removeClass('show');
5666 ce.addClass('collapsing');
5668 ce.on('transitionend', function() {
5669 ce.dom.style.height = '';
5670 ce.removeClass('collapsing');
5671 ce.addClass('collapse');
5672 }, this, { single: true} );
5692 * @class Roo.bootstrap.NavSimplebar
5693 * @extends Roo.bootstrap.Navbar
5694 * Bootstrap Sidebar class
5696 * @cfg {Boolean} inverse is inverted color
5698 * @cfg {String} type (nav | pills | tabs)
5699 * @cfg {Boolean} arrangement stacked | justified
5700 * @cfg {String} align (left | right) alignment
5702 * @cfg {Boolean} main (true|false) main nav bar? default false
5703 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5705 * @cfg {String} tag (header|footer|nav|div) default is nav
5707 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5711 * Create a new Sidebar
5712 * @param {Object} config The config object
5716 Roo.bootstrap.NavSimplebar = function(config){
5717 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5720 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5736 getAutoCreate : function(){
5740 tag : this.tag || 'div',
5741 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5743 if (['light','white'].indexOf(this.weight) > -1) {
5744 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5746 cfg.cls += ' bg-' + this.weight;
5749 cfg.cls += ' navbar-inverse';
5753 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5755 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5764 cls: 'nav nav-' + this.xtype,
5770 this.type = this.type || 'nav';
5771 if (['tabs','pills'].indexOf(this.type) != -1) {
5772 cfg.cn[0].cls += ' nav-' + this.type
5776 if (this.type!=='nav') {
5777 Roo.log('nav type must be nav/tabs/pills')
5779 cfg.cn[0].cls += ' navbar-nav'
5785 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5786 cfg.cn[0].cls += ' nav-' + this.arrangement;
5790 if (this.align === 'right') {
5791 cfg.cn[0].cls += ' navbar-right';
5816 * navbar-expand-md fixed-top
5820 * @class Roo.bootstrap.NavHeaderbar
5821 * @extends Roo.bootstrap.NavSimplebar
5822 * Bootstrap Sidebar class
5824 * @cfg {String} brand what is brand
5825 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5826 * @cfg {String} brand_href href of the brand
5827 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5828 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5829 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5830 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5833 * Create a new Sidebar
5834 * @param {Object} config The config object
5838 Roo.bootstrap.NavHeaderbar = function(config){
5839 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5843 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5850 desktopCenter : false,
5853 getAutoCreate : function(){
5856 tag: this.nav || 'nav',
5857 cls: 'navbar navbar-expand-md',
5863 if (this.desktopCenter) {
5864 cn.push({cls : 'container', cn : []});
5872 cls: 'navbar-toggle navbar-toggler',
5873 'data-toggle': 'collapse',
5878 html: 'Toggle navigation'
5882 cls: 'icon-bar navbar-toggler-icon'
5895 cn.push( Roo.bootstrap.version == 4 ? btn : {
5897 cls: 'navbar-header',
5906 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5910 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5912 if (['light','white'].indexOf(this.weight) > -1) {
5913 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5915 cfg.cls += ' bg-' + this.weight;
5918 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5919 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5921 // tag can override this..
5923 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5926 if (this.brand !== '') {
5927 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5928 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5930 href: this.brand_href ? this.brand_href : '#',
5931 cls: 'navbar-brand',
5939 cfg.cls += ' main-nav';
5947 getHeaderChildContainer : function()
5949 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5950 return this.el.select('.navbar-header',true).first();
5953 return this.getChildContainer();
5956 getChildContainer : function()
5959 return this.el.select('.roo-navbar-collapse',true).first();
5964 initEvents : function()
5966 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5968 if (this.autohide) {
5973 Roo.get(document).on('scroll',function(e) {
5974 var ns = Roo.get(document).getScroll().top;
5975 var os = prevScroll;
5979 ft.removeClass('slideDown');
5980 ft.addClass('slideUp');
5983 ft.removeClass('slideUp');
5984 ft.addClass('slideDown');
6005 * @class Roo.bootstrap.NavSidebar
6006 * @extends Roo.bootstrap.Navbar
6007 * Bootstrap Sidebar class
6010 * Create a new Sidebar
6011 * @param {Object} config The config object
6015 Roo.bootstrap.NavSidebar = function(config){
6016 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6019 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6021 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6023 getAutoCreate : function(){
6028 cls: 'sidebar sidebar-nav'
6050 * @class Roo.bootstrap.NavGroup
6051 * @extends Roo.bootstrap.Component
6052 * Bootstrap NavGroup class
6053 * @cfg {String} align (left|right)
6054 * @cfg {Boolean} inverse
6055 * @cfg {String} type (nav|pills|tab) default nav
6056 * @cfg {String} navId - reference Id for navbar.
6057 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6060 * Create a new nav group
6061 * @param {Object} config The config object
6064 Roo.bootstrap.NavGroup = function(config){
6065 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6068 Roo.bootstrap.NavGroup.register(this);
6072 * Fires when the active item changes
6073 * @param {Roo.bootstrap.NavGroup} this
6074 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6075 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6082 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6094 getAutoCreate : function()
6096 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6102 if (Roo.bootstrap.version == 4) {
6103 if (['tabs','pills'].indexOf(this.type) != -1) {
6104 cfg.cls += ' nav-' + this.type;
6106 // trying to remove so header bar can right align top?
6107 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6108 // do not use on header bar...
6109 cfg.cls += ' navbar-nav';
6114 if (['tabs','pills'].indexOf(this.type) != -1) {
6115 cfg.cls += ' nav-' + this.type
6117 if (this.type !== 'nav') {
6118 Roo.log('nav type must be nav/tabs/pills')
6120 cfg.cls += ' navbar-nav'
6124 if (this.parent() && this.parent().sidebar) {
6127 cls: 'dashboard-menu sidebar-menu'
6133 if (this.form === true) {
6136 cls: 'navbar-form form-inline'
6138 //nav navbar-right ml-md-auto
6139 if (this.align === 'right') {
6140 cfg.cls += ' navbar-right ml-md-auto';
6142 cfg.cls += ' navbar-left';
6146 if (this.align === 'right') {
6147 cfg.cls += ' navbar-right ml-md-auto';
6149 cfg.cls += ' mr-auto';
6153 cfg.cls += ' navbar-inverse';
6161 * sets the active Navigation item
6162 * @param {Roo.bootstrap.NavItem} the new current navitem
6164 setActiveItem : function(item)
6167 Roo.each(this.navItems, function(v){
6172 v.setActive(false, true);
6179 item.setActive(true, true);
6180 this.fireEvent('changed', this, item, prev);
6185 * gets the active Navigation item
6186 * @return {Roo.bootstrap.NavItem} the current navitem
6188 getActive : function()
6192 Roo.each(this.navItems, function(v){
6203 indexOfNav : function()
6207 Roo.each(this.navItems, function(v,i){
6218 * adds a Navigation item
6219 * @param {Roo.bootstrap.NavItem} the navitem to add
6221 addItem : function(cfg)
6223 if (this.form && Roo.bootstrap.version == 4) {
6226 var cn = new Roo.bootstrap.NavItem(cfg);
6228 cn.parentId = this.id;
6229 cn.onRender(this.el, null);
6233 * register a Navigation item
6234 * @param {Roo.bootstrap.NavItem} the navitem to add
6236 register : function(item)
6238 this.navItems.push( item);
6239 item.navId = this.navId;
6244 * clear all the Navigation item
6247 clearAll : function()
6250 this.el.dom.innerHTML = '';
6253 getNavItem: function(tabId)
6256 Roo.each(this.navItems, function(e) {
6257 if (e.tabId == tabId) {
6267 setActiveNext : function()
6269 var i = this.indexOfNav(this.getActive());
6270 if (i > this.navItems.length) {
6273 this.setActiveItem(this.navItems[i+1]);
6275 setActivePrev : function()
6277 var i = this.indexOfNav(this.getActive());
6281 this.setActiveItem(this.navItems[i-1]);
6283 clearWasActive : function(except) {
6284 Roo.each(this.navItems, function(e) {
6285 if (e.tabId != except.tabId && e.was_active) {
6286 e.was_active = false;
6293 getWasActive : function ()
6296 Roo.each(this.navItems, function(e) {
6311 Roo.apply(Roo.bootstrap.NavGroup, {
6315 * register a Navigation Group
6316 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6318 register : function(navgrp)
6320 this.groups[navgrp.navId] = navgrp;
6324 * fetch a Navigation Group based on the navigation ID
6325 * @param {string} the navgroup to add
6326 * @returns {Roo.bootstrap.NavGroup} the navgroup
6328 get: function(navId) {
6329 if (typeof(this.groups[navId]) == 'undefined') {
6331 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6333 return this.groups[navId] ;
6348 * @class Roo.bootstrap.NavItem
6349 * @extends Roo.bootstrap.Component
6350 * Bootstrap Navbar.NavItem class
6351 * @cfg {String} href link to
6352 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6353 * @cfg {Boolean} button_outline show and outlined button
6354 * @cfg {String} html content of button
6355 * @cfg {String} badge text inside badge
6356 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6357 * @cfg {String} glyphicon DEPRICATED - use fa
6358 * @cfg {String} icon DEPRICATED - use fa
6359 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6360 * @cfg {Boolean} active Is item active
6361 * @cfg {Boolean} disabled Is item disabled
6362 * @cfg {String} linkcls Link Class
6363 * @cfg {Boolean} preventDefault (true | false) default false
6364 * @cfg {String} tabId the tab that this item activates.
6365 * @cfg {String} tagtype (a|span) render as a href or span?
6366 * @cfg {Boolean} animateRef (true|false) link to element default false
6369 * Create a new Navbar Item
6370 * @param {Object} config The config object
6372 Roo.bootstrap.NavItem = function(config){
6373 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6378 * The raw click event for the entire grid.
6379 * @param {Roo.EventObject} e
6384 * Fires when the active item active state changes
6385 * @param {Roo.bootstrap.NavItem} this
6386 * @param {boolean} state the new state
6392 * Fires when scroll to element
6393 * @param {Roo.bootstrap.NavItem} this
6394 * @param {Object} options
6395 * @param {Roo.EventObject} e
6403 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6412 preventDefault : false,
6420 button_outline : false,
6424 getAutoCreate : function(){
6431 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6434 cfg.cls += ' active' ;
6436 if (this.disabled) {
6437 cfg.cls += ' disabled';
6441 if (this.button_weight.length) {
6442 cfg.tag = this.href ? 'a' : 'button';
6443 cfg.html = this.html || '';
6444 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6446 cfg.href = this.href;
6449 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6451 cfg.cls += " nav-html";
6454 // menu .. should add dropdown-menu class - so no need for carat..
6456 if (this.badge !== '') {
6458 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6463 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6467 href : this.href || "#",
6468 html: this.html || '',
6472 if (this.tagtype == 'a') {
6473 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6477 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6478 } else if (this.fa) {
6479 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6480 } else if(this.glyphicon) {
6481 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6483 cfg.cn[0].cls += " nav-html";
6487 cfg.cn[0].html += " <span class='caret'></span>";
6491 if (this.badge !== '') {
6492 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6500 onRender : function(ct, position)
6502 // Roo.log("Call onRender: " + this.xtype);
6503 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6507 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6508 this.navLink = this.el.select('.nav-link',true).first();
6509 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6514 initEvents: function()
6516 if (typeof (this.menu) != 'undefined') {
6517 this.menu.parentType = this.xtype;
6518 this.menu.triggerEl = this.el;
6519 this.menu = this.addxtype(Roo.apply({}, this.menu));
6522 this.el.on('click', this.onClick, this);
6524 //if(this.tagtype == 'span'){
6525 // this.el.select('span',true).on('click', this.onClick, this);
6528 // at this point parent should be available..
6529 this.parent().register(this);
6532 onClick : function(e)
6534 if (e.getTarget('.dropdown-menu-item')) {
6535 // did you click on a menu itemm.... - then don't trigger onclick..
6540 this.preventDefault ||
6543 Roo.log("NavItem - prevent Default?");
6547 if (this.disabled) {
6551 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6552 if (tg && tg.transition) {
6553 Roo.log("waiting for the transitionend");
6559 //Roo.log("fire event clicked");
6560 if(this.fireEvent('click', this, e) === false){
6564 if(this.tagtype == 'span'){
6568 //Roo.log(this.href);
6569 var ael = this.el.select('a',true).first();
6572 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6573 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6574 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6575 return; // ignore... - it's a 'hash' to another page.
6577 Roo.log("NavItem - prevent Default?");
6579 this.scrollToElement(e);
6583 var p = this.parent();
6585 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6586 if (typeof(p.setActiveItem) !== 'undefined') {
6587 p.setActiveItem(this);
6591 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6592 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6593 // remove the collapsed menu expand...
6594 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6598 isActive: function () {
6601 setActive : function(state, fire, is_was_active)
6603 if (this.active && !state && this.navId) {
6604 this.was_active = true;
6605 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6607 nv.clearWasActive(this);
6611 this.active = state;
6614 this.el.removeClass('active');
6615 this.navLink ? this.navLink.removeClass('active') : false;
6616 } else if (!this.el.hasClass('active')) {
6618 this.el.addClass('active');
6619 if (Roo.bootstrap.version == 4 && this.navLink ) {
6620 this.navLink.addClass('active');
6625 this.fireEvent('changed', this, state);
6628 // show a panel if it's registered and related..
6630 if (!this.navId || !this.tabId || !state || is_was_active) {
6634 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6638 var pan = tg.getPanelByName(this.tabId);
6642 // if we can not flip to new panel - go back to old nav highlight..
6643 if (false == tg.showPanel(pan)) {
6644 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6646 var onav = nv.getWasActive();
6648 onav.setActive(true, false, true);
6657 // this should not be here...
6658 setDisabled : function(state)
6660 this.disabled = state;
6662 this.el.removeClass('disabled');
6663 } else if (!this.el.hasClass('disabled')) {
6664 this.el.addClass('disabled');
6670 * Fetch the element to display the tooltip on.
6671 * @return {Roo.Element} defaults to this.el
6673 tooltipEl : function()
6675 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6678 scrollToElement : function(e)
6680 var c = document.body;
6683 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6685 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6686 c = document.documentElement;
6689 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6695 var o = target.calcOffsetsTo(c);
6702 this.fireEvent('scrollto', this, options, e);
6704 Roo.get(c).scrollTo('top', options.value, true);
6709 * Set the HTML (text content) of the item
6710 * @param {string} html content for the nav item
6712 setHtml : function(html)
6715 this.htmlEl.dom.innerHTML = html;
6727 * <span> icon </span>
6728 * <span> text </span>
6729 * <span>badge </span>
6733 * @class Roo.bootstrap.NavSidebarItem
6734 * @extends Roo.bootstrap.NavItem
6735 * Bootstrap Navbar.NavSidebarItem class
6736 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6737 * {Boolean} open is the menu open
6738 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6739 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6740 * {String} buttonSize (sm|md|lg)the extra classes for the button
6741 * {Boolean} showArrow show arrow next to the text (default true)
6743 * Create a new Navbar Button
6744 * @param {Object} config The config object
6746 Roo.bootstrap.NavSidebarItem = function(config){
6747 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6752 * The raw click event for the entire grid.
6753 * @param {Roo.EventObject} e
6758 * Fires when the active item active state changes
6759 * @param {Roo.bootstrap.NavSidebarItem} this
6760 * @param {boolean} state the new state
6768 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6770 badgeWeight : 'default',
6776 buttonWeight : 'default',
6782 getAutoCreate : function(){
6787 href : this.href || '#',
6793 if(this.buttonView){
6796 href : this.href || '#',
6797 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6810 cfg.cls += ' active';
6813 if (this.disabled) {
6814 cfg.cls += ' disabled';
6817 cfg.cls += ' open x-open';
6820 if (this.glyphicon || this.icon) {
6821 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6822 a.cn.push({ tag : 'i', cls : c }) ;
6825 if(!this.buttonView){
6828 html : this.html || ''
6835 if (this.badge !== '') {
6836 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6842 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6845 a.cls += ' dropdown-toggle treeview' ;
6851 initEvents : function()
6853 if (typeof (this.menu) != 'undefined') {
6854 this.menu.parentType = this.xtype;
6855 this.menu.triggerEl = this.el;
6856 this.menu = this.addxtype(Roo.apply({}, this.menu));
6859 this.el.on('click', this.onClick, this);
6861 if(this.badge !== ''){
6862 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6867 onClick : function(e)
6874 if(this.preventDefault){
6878 this.fireEvent('click', this, e);
6881 disable : function()
6883 this.setDisabled(true);
6888 this.setDisabled(false);
6891 setDisabled : function(state)
6893 if(this.disabled == state){
6897 this.disabled = state;
6900 this.el.addClass('disabled');
6904 this.el.removeClass('disabled');
6909 setActive : function(state)
6911 if(this.active == state){
6915 this.active = state;
6918 this.el.addClass('active');
6922 this.el.removeClass('active');
6927 isActive: function ()
6932 setBadge : function(str)
6938 this.badgeEl.dom.innerHTML = str;
6953 Roo.namespace('Roo.bootstrap.breadcrumb');
6957 * @class Roo.bootstrap.breadcrumb.Nav
6958 * @extends Roo.bootstrap.Component
6959 * Bootstrap Breadcrumb Nav Class
6961 * @children Roo.bootstrap.breadcrumb.Item
6964 * Create a new breadcrumb.Nav
6965 * @param {Object} config The config object
6969 Roo.bootstrap.breadcrumb.Nav = function(config){
6970 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6975 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6977 getAutoCreate : function()
6994 initEvents: function()
6996 this.olEl = this.el.select('ol',true).first();
6998 getChildContainer : function()
7014 * @class Roo.bootstrap.breadcrumb.Nav
7015 * @extends Roo.bootstrap.Component
7016 * Bootstrap Breadcrumb Nav Class
7018 * @children Roo.bootstrap.breadcrumb.Component
7019 * @cfg {String} html the content of the link.
7020 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7021 * @cfg {Boolean} active is it active
7025 * Create a new breadcrumb.Nav
7026 * @param {Object} config The config object
7029 Roo.bootstrap.breadcrumb.Item = function(config){
7030 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7035 * The img click event for the img.
7036 * @param {Roo.EventObject} e
7043 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7048 getAutoCreate : function()
7053 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7055 if (this.href !== false) {
7062 cfg.html = this.html;
7068 initEvents: function()
7071 this.el.select('a', true).first().on('click',this.onClick, this)
7075 onClick : function(e)
7078 this.fireEvent('click',this, e);
7091 * @class Roo.bootstrap.Row
7092 * @extends Roo.bootstrap.Component
7093 * Bootstrap Row class (contains columns...)
7097 * @param {Object} config The config object
7100 Roo.bootstrap.Row = function(config){
7101 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7104 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7106 getAutoCreate : function(){
7125 * @class Roo.bootstrap.Pagination
7126 * @extends Roo.bootstrap.Component
7127 * Bootstrap Pagination class
7128 * @cfg {String} size xs | sm | md | lg
7129 * @cfg {Boolean} inverse false | true
7132 * Create a new Pagination
7133 * @param {Object} config The config object
7136 Roo.bootstrap.Pagination = function(config){
7137 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7140 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7146 getAutoCreate : function(){
7152 cfg.cls += ' inverse';
7158 cfg.cls += " " + this.cls;
7176 * @class Roo.bootstrap.PaginationItem
7177 * @extends Roo.bootstrap.Component
7178 * Bootstrap PaginationItem class
7179 * @cfg {String} html text
7180 * @cfg {String} href the link
7181 * @cfg {Boolean} preventDefault (true | false) default true
7182 * @cfg {Boolean} active (true | false) default false
7183 * @cfg {Boolean} disabled default false
7187 * Create a new PaginationItem
7188 * @param {Object} config The config object
7192 Roo.bootstrap.PaginationItem = function(config){
7193 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7198 * The raw click event for the entire grid.
7199 * @param {Roo.EventObject} e
7205 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7209 preventDefault: true,
7214 getAutoCreate : function(){
7220 href : this.href ? this.href : '#',
7221 html : this.html ? this.html : ''
7231 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7235 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7241 initEvents: function() {
7243 this.el.on('click', this.onClick, this);
7246 onClick : function(e)
7248 Roo.log('PaginationItem on click ');
7249 if(this.preventDefault){
7257 this.fireEvent('click', this, e);
7273 * @class Roo.bootstrap.Slider
7274 * @extends Roo.bootstrap.Component
7275 * Bootstrap Slider class
7278 * Create a new Slider
7279 * @param {Object} config The config object
7282 Roo.bootstrap.Slider = function(config){
7283 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7286 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7288 getAutoCreate : function(){
7292 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7296 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7308 * Ext JS Library 1.1.1
7309 * Copyright(c) 2006-2007, Ext JS, LLC.
7311 * Originally Released Under LGPL - original licence link has changed is not relivant.
7314 * <script type="text/javascript">
7317 * @extends Roo.dd.DDProxy
7318 * @class Roo.grid.SplitDragZone
7319 * Support for Column Header resizing
7321 * @param {Object} config
7324 // This is a support class used internally by the Grid components
7325 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7327 this.view = grid.getView();
7328 this.proxy = this.view.resizeProxy;
7329 Roo.grid.SplitDragZone.superclass.constructor.call(
7332 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7334 dragElId : Roo.id(this.proxy.dom),
7339 this.setHandleElId(Roo.id(hd));
7340 if (hd2 !== false) {
7341 this.setOuterHandleElId(Roo.id(hd2));
7344 this.scroll = false;
7346 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7347 fly: Roo.Element.fly,
7349 b4StartDrag : function(x, y){
7350 this.view.headersDisabled = true;
7351 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7352 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7354 this.proxy.setHeight(h);
7356 // for old system colWidth really stored the actual width?
7357 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7358 // which in reality did not work.. - it worked only for fixed sizes
7359 // for resizable we need to use actual sizes.
7360 var w = this.cm.getColumnWidth(this.cellIndex);
7361 if (!this.view.mainWrap) {
7363 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7368 // this was w-this.grid.minColumnWidth;
7369 // doesnt really make sense? - w = thie curren width or the rendered one?
7370 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7371 this.resetConstraints();
7372 this.setXConstraint(minw, 1000);
7373 this.setYConstraint(0, 0);
7374 this.minX = x - minw;
7375 this.maxX = x + 1000;
7377 if (!this.view.mainWrap) { // this is Bootstrap code..
7378 this.getDragEl().style.display='block';
7381 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7385 handleMouseDown : function(e){
7386 ev = Roo.EventObject.setEvent(e);
7387 var t = this.fly(ev.getTarget());
7388 if(t.hasClass("x-grid-split")){
7389 this.cellIndex = this.view.getCellIndex(t.dom);
7391 this.cm = this.grid.colModel;
7392 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7393 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7398 endDrag : function(e){
7399 this.view.headersDisabled = false;
7400 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7401 var diff = endX - this.startPos;
7403 var w = this.cm.getColumnWidth(this.cellIndex);
7404 if (!this.view.mainWrap) {
7407 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7410 autoOffset : function(){
7415 * Ext JS Library 1.1.1
7416 * Copyright(c) 2006-2007, Ext JS, LLC.
7418 * Originally Released Under LGPL - original licence link has changed is not relivant.
7421 * <script type="text/javascript">
7425 * @class Roo.grid.AbstractSelectionModel
7426 * @extends Roo.util.Observable
7428 * Abstract base class for grid SelectionModels. It provides the interface that should be
7429 * implemented by descendant classes. This class should not be directly instantiated.
7432 Roo.grid.AbstractSelectionModel = function(){
7433 this.locked = false;
7434 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7437 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7438 /** @ignore Called by the grid automatically. Do not call directly. */
7439 init : function(grid){
7445 * Locks the selections.
7452 * Unlocks the selections.
7454 unlock : function(){
7455 this.locked = false;
7459 * Returns true if the selections are locked.
7462 isLocked : function(){
7467 * Ext JS Library 1.1.1
7468 * Copyright(c) 2006-2007, Ext JS, LLC.
7470 * Originally Released Under LGPL - original licence link has changed is not relivant.
7473 * <script type="text/javascript">
7476 * @extends Roo.grid.AbstractSelectionModel
7477 * @class Roo.grid.RowSelectionModel
7478 * The default SelectionModel used by {@link Roo.grid.Grid}.
7479 * It supports multiple selections and keyboard selection/navigation.
7481 * @param {Object} config
7483 Roo.grid.RowSelectionModel = function(config){
7484 Roo.apply(this, config);
7485 this.selections = new Roo.util.MixedCollection(false, function(o){
7490 this.lastActive = false;
7494 * @event selectionchange
7495 * Fires when the selection changes
7496 * @param {SelectionModel} this
7498 "selectionchange" : true,
7500 * @event afterselectionchange
7501 * Fires after the selection changes (eg. by key press or clicking)
7502 * @param {SelectionModel} this
7504 "afterselectionchange" : true,
7506 * @event beforerowselect
7507 * Fires when a row is selected being selected, return false to cancel.
7508 * @param {SelectionModel} this
7509 * @param {Number} rowIndex The selected index
7510 * @param {Boolean} keepExisting False if other selections will be cleared
7512 "beforerowselect" : true,
7515 * Fires when a row is selected.
7516 * @param {SelectionModel} this
7517 * @param {Number} rowIndex The selected index
7518 * @param {Roo.data.Record} r The record
7522 * @event rowdeselect
7523 * Fires when a row is deselected.
7524 * @param {SelectionModel} this
7525 * @param {Number} rowIndex The selected index
7527 "rowdeselect" : true
7529 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7530 this.locked = false;
7533 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7535 * @cfg {Boolean} singleSelect
7536 * True to allow selection of only one row at a time (defaults to false)
7538 singleSelect : false,
7541 initEvents : function(){
7543 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7544 this.grid.on("mousedown", this.handleMouseDown, this);
7545 }else{ // allow click to work like normal
7546 this.grid.on("rowclick", this.handleDragableRowClick, this);
7548 // bootstrap does not have a view..
7549 var view = this.grid.view ? this.grid.view : this.grid;
7550 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7553 this.selectPrevious(e.shiftKey);
7554 }else if(this.last !== false && this.lastActive !== false){
7555 var last = this.last;
7556 this.selectRange(this.last, this.lastActive-1);
7557 view.focusRow(this.lastActive);
7562 this.selectFirstRow();
7564 this.fireEvent("afterselectionchange", this);
7566 "down" : function(e){
7568 this.selectNext(e.shiftKey);
7569 }else if(this.last !== false && this.lastActive !== false){
7570 var last = this.last;
7571 this.selectRange(this.last, this.lastActive+1);
7572 view.focusRow(this.lastActive);
7577 this.selectFirstRow();
7579 this.fireEvent("afterselectionchange", this);
7585 view.on("refresh", this.onRefresh, this);
7586 view.on("rowupdated", this.onRowUpdated, this);
7587 view.on("rowremoved", this.onRemove, this);
7591 onRefresh : function(){
7592 var ds = this.grid.ds, i, v = this.grid.view;
7593 var s = this.selections;
7595 if((i = ds.indexOfId(r.id)) != -1){
7597 s.add(ds.getAt(i)); // updating the selection relate data
7605 onRemove : function(v, index, r){
7606 this.selections.remove(r);
7610 onRowUpdated : function(v, index, r){
7611 if(this.isSelected(r)){
7612 v.onRowSelect(index);
7618 * @param {Array} records The records to select
7619 * @param {Boolean} keepExisting (optional) True to keep existing selections
7621 selectRecords : function(records, keepExisting){
7623 this.clearSelections();
7625 var ds = this.grid.ds;
7626 for(var i = 0, len = records.length; i < len; i++){
7627 this.selectRow(ds.indexOf(records[i]), true);
7632 * Gets the number of selected rows.
7635 getCount : function(){
7636 return this.selections.length;
7640 * Selects the first row in the grid.
7642 selectFirstRow : function(){
7647 * Select the last row.
7648 * @param {Boolean} keepExisting (optional) True to keep existing selections
7650 selectLastRow : function(keepExisting){
7651 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7655 * Selects the row immediately following the last selected row.
7656 * @param {Boolean} keepExisting (optional) True to keep existing selections
7658 selectNext : function(keepExisting){
7659 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7660 this.selectRow(this.last+1, keepExisting);
7661 var view = this.grid.view ? this.grid.view : this.grid;
7662 view.focusRow(this.last);
7667 * Selects the row that precedes the last selected row.
7668 * @param {Boolean} keepExisting (optional) True to keep existing selections
7670 selectPrevious : function(keepExisting){
7672 this.selectRow(this.last-1, keepExisting);
7673 var view = this.grid.view ? this.grid.view : this.grid;
7674 view.focusRow(this.last);
7679 * Returns the selected records
7680 * @return {Array} Array of selected records
7682 getSelections : function(){
7683 return [].concat(this.selections.items);
7687 * Returns the first selected record.
7690 getSelected : function(){
7691 return this.selections.itemAt(0);
7696 * Clears all selections.
7698 clearSelections : function(fast){
7703 var ds = this.grid.ds;
7704 var s = this.selections;
7706 this.deselectRow(ds.indexOfId(r.id));
7710 this.selections.clear();
7719 selectAll : function(){
7723 this.selections.clear();
7724 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7725 this.selectRow(i, true);
7730 * Returns True if there is a selection.
7733 hasSelection : function(){
7734 return this.selections.length > 0;
7738 * Returns True if the specified row is selected.
7739 * @param {Number/Record} record The record or index of the record to check
7742 isSelected : function(index){
7743 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7744 return (r && this.selections.key(r.id) ? true : false);
7748 * Returns True if the specified record id is selected.
7749 * @param {String} id The id of record to check
7752 isIdSelected : function(id){
7753 return (this.selections.key(id) ? true : false);
7757 handleMouseDown : function(e, t)
7759 var view = this.grid.view ? this.grid.view : this.grid;
7761 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7764 if(e.shiftKey && this.last !== false){
7765 var last = this.last;
7766 this.selectRange(last, rowIndex, e.ctrlKey);
7767 this.last = last; // reset the last
7768 view.focusRow(rowIndex);
7770 var isSelected = this.isSelected(rowIndex);
7771 if(e.button !== 0 && isSelected){
7772 view.focusRow(rowIndex);
7773 }else if(e.ctrlKey && isSelected){
7774 this.deselectRow(rowIndex);
7775 }else if(!isSelected){
7776 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7777 view.focusRow(rowIndex);
7780 this.fireEvent("afterselectionchange", this);
7783 handleDragableRowClick : function(grid, rowIndex, e)
7785 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7786 this.selectRow(rowIndex, false);
7787 var view = this.grid.view ? this.grid.view : this.grid;
7788 view.focusRow(rowIndex);
7789 this.fireEvent("afterselectionchange", this);
7794 * Selects multiple rows.
7795 * @param {Array} rows Array of the indexes of the row to select
7796 * @param {Boolean} keepExisting (optional) True to keep existing selections
7798 selectRows : function(rows, keepExisting){
7800 this.clearSelections();
7802 for(var i = 0, len = rows.length; i < len; i++){
7803 this.selectRow(rows[i], true);
7808 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7809 * @param {Number} startRow The index of the first row in the range
7810 * @param {Number} endRow The index of the last row in the range
7811 * @param {Boolean} keepExisting (optional) True to retain existing selections
7813 selectRange : function(startRow, endRow, keepExisting){
7818 this.clearSelections();
7820 if(startRow <= endRow){
7821 for(var i = startRow; i <= endRow; i++){
7822 this.selectRow(i, true);
7825 for(var i = startRow; i >= endRow; i--){
7826 this.selectRow(i, true);
7832 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7833 * @param {Number} startRow The index of the first row in the range
7834 * @param {Number} endRow The index of the last row in the range
7836 deselectRange : function(startRow, endRow, preventViewNotify){
7840 for(var i = startRow; i <= endRow; i++){
7841 this.deselectRow(i, preventViewNotify);
7847 * @param {Number} row The index of the row to select
7848 * @param {Boolean} keepExisting (optional) True to keep existing selections
7850 selectRow : function(index, keepExisting, preventViewNotify){
7851 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7854 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7855 if(!keepExisting || this.singleSelect){
7856 this.clearSelections();
7858 var r = this.grid.ds.getAt(index);
7859 this.selections.add(r);
7860 this.last = this.lastActive = index;
7861 if(!preventViewNotify){
7862 var view = this.grid.view ? this.grid.view : this.grid;
7863 view.onRowSelect(index);
7865 this.fireEvent("rowselect", this, index, r);
7866 this.fireEvent("selectionchange", this);
7872 * @param {Number} row The index of the row to deselect
7874 deselectRow : function(index, preventViewNotify){
7878 if(this.last == index){
7881 if(this.lastActive == index){
7882 this.lastActive = false;
7884 var r = this.grid.ds.getAt(index);
7885 this.selections.remove(r);
7886 if(!preventViewNotify){
7887 var view = this.grid.view ? this.grid.view : this.grid;
7888 view.onRowDeselect(index);
7890 this.fireEvent("rowdeselect", this, index);
7891 this.fireEvent("selectionchange", this);
7895 restoreLast : function(){
7897 this.last = this._last;
7902 acceptsNav : function(row, col, cm){
7903 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7907 onEditorKey : function(field, e){
7908 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7913 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7915 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7917 }else if(k == e.ENTER && !e.ctrlKey){
7921 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7923 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7925 }else if(k == e.ESC){
7929 g.startEditing(newCell[0], newCell[1]);
7934 * Ext JS Library 1.1.1
7935 * Copyright(c) 2006-2007, Ext JS, LLC.
7937 * Originally Released Under LGPL - original licence link has changed is not relivant.
7940 * <script type="text/javascript">
7945 * @class Roo.grid.ColumnModel
7946 * @extends Roo.util.Observable
7947 * This is the default implementation of a ColumnModel used by the Grid. It defines
7948 * the columns in the grid.
7951 var colModel = new Roo.grid.ColumnModel([
7952 {header: "Ticker", width: 60, sortable: true, locked: true},
7953 {header: "Company Name", width: 150, sortable: true},
7954 {header: "Market Cap.", width: 100, sortable: true},
7955 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7956 {header: "Employees", width: 100, sortable: true, resizable: false}
7961 * The config options listed for this class are options which may appear in each
7962 * individual column definition.
7963 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7965 * @param {Object} config An Array of column config objects. See this class's
7966 * config objects for details.
7968 Roo.grid.ColumnModel = function(config){
7970 * The config passed into the constructor
7972 this.config = []; //config;
7975 // if no id, create one
7976 // if the column does not have a dataIndex mapping,
7977 // map it to the order it is in the config
7978 for(var i = 0, len = config.length; i < len; i++){
7979 this.addColumn(config[i]);
7984 * The width of columns which have no width specified (defaults to 100)
7987 this.defaultWidth = 100;
7990 * Default sortable of columns which have no sortable specified (defaults to false)
7993 this.defaultSortable = false;
7997 * @event widthchange
7998 * Fires when the width of a column changes.
7999 * @param {ColumnModel} this
8000 * @param {Number} columnIndex The column index
8001 * @param {Number} newWidth The new width
8003 "widthchange": true,
8005 * @event headerchange
8006 * Fires when the text of a header changes.
8007 * @param {ColumnModel} this
8008 * @param {Number} columnIndex The column index
8009 * @param {Number} newText The new header text
8011 "headerchange": true,
8013 * @event hiddenchange
8014 * Fires when a column is hidden or "unhidden".
8015 * @param {ColumnModel} this
8016 * @param {Number} columnIndex The column index
8017 * @param {Boolean} hidden true if hidden, false otherwise
8019 "hiddenchange": true,
8021 * @event columnmoved
8022 * Fires when a column is moved.
8023 * @param {ColumnModel} this
8024 * @param {Number} oldIndex
8025 * @param {Number} newIndex
8027 "columnmoved" : true,
8029 * @event columlockchange
8030 * Fires when a column's locked state is changed
8031 * @param {ColumnModel} this
8032 * @param {Number} colIndex
8033 * @param {Boolean} locked true if locked
8035 "columnlockchange" : true
8037 Roo.grid.ColumnModel.superclass.constructor.call(this);
8039 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8041 * @cfg {String} header The header text to display in the Grid view.
8044 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8047 * @cfg {String} smHeader Header at Bootsrap Small width
8050 * @cfg {String} mdHeader Header at Bootsrap Medium width
8053 * @cfg {String} lgHeader Header at Bootsrap Large width
8056 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8059 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8060 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8061 * specified, the column's index is used as an index into the Record's data Array.
8064 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8065 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8068 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8069 * Defaults to the value of the {@link #defaultSortable} property.
8070 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8073 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8076 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8079 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8082 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8085 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8086 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8087 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8088 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8091 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8094 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8097 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8100 * @cfg {String} cursor (Optional)
8103 * @cfg {String} tooltip (Optional)
8106 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8109 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8112 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8115 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8118 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8121 * Returns the id of the column at the specified index.
8122 * @param {Number} index The column index
8123 * @return {String} the id
8125 getColumnId : function(index){
8126 return this.config[index].id;
8130 * Returns the column for a specified id.
8131 * @param {String} id The column id
8132 * @return {Object} the column
8134 getColumnById : function(id){
8135 return this.lookup[id];
8140 * Returns the column Object for a specified dataIndex.
8141 * @param {String} dataIndex The column dataIndex
8142 * @return {Object|Boolean} the column or false if not found
8144 getColumnByDataIndex: function(dataIndex){
8145 var index = this.findColumnIndex(dataIndex);
8146 return index > -1 ? this.config[index] : false;
8150 * Returns the index for a specified column id.
8151 * @param {String} id The column id
8152 * @return {Number} the index, or -1 if not found
8154 getIndexById : function(id){
8155 for(var i = 0, len = this.config.length; i < len; i++){
8156 if(this.config[i].id == id){
8164 * Returns the index for a specified column dataIndex.
8165 * @param {String} dataIndex The column dataIndex
8166 * @return {Number} the index, or -1 if not found
8169 findColumnIndex : function(dataIndex){
8170 for(var i = 0, len = this.config.length; i < len; i++){
8171 if(this.config[i].dataIndex == dataIndex){
8179 moveColumn : function(oldIndex, newIndex){
8180 var c = this.config[oldIndex];
8181 this.config.splice(oldIndex, 1);
8182 this.config.splice(newIndex, 0, c);
8183 this.dataMap = null;
8184 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8187 isLocked : function(colIndex){
8188 return this.config[colIndex].locked === true;
8191 setLocked : function(colIndex, value, suppressEvent){
8192 if(this.isLocked(colIndex) == value){
8195 this.config[colIndex].locked = value;
8197 this.fireEvent("columnlockchange", this, colIndex, value);
8201 getTotalLockedWidth : function(){
8203 for(var i = 0; i < this.config.length; i++){
8204 if(this.isLocked(i) && !this.isHidden(i)){
8205 this.totalWidth += this.getColumnWidth(i);
8211 getLockedCount : function(){
8212 for(var i = 0, len = this.config.length; i < len; i++){
8213 if(!this.isLocked(i)){
8218 return this.config.length;
8222 * Returns the number of columns.
8225 getColumnCount : function(visibleOnly){
8226 if(visibleOnly === true){
8228 for(var i = 0, len = this.config.length; i < len; i++){
8229 if(!this.isHidden(i)){
8235 return this.config.length;
8239 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8240 * @param {Function} fn
8241 * @param {Object} scope (optional)
8242 * @return {Array} result
8244 getColumnsBy : function(fn, scope){
8246 for(var i = 0, len = this.config.length; i < len; i++){
8247 var c = this.config[i];
8248 if(fn.call(scope||this, c, i) === true){
8256 * Returns true if the specified column is sortable.
8257 * @param {Number} col The column index
8260 isSortable : function(col){
8261 if(typeof this.config[col].sortable == "undefined"){
8262 return this.defaultSortable;
8264 return this.config[col].sortable;
8268 * Returns the rendering (formatting) function defined for the column.
8269 * @param {Number} col The column index.
8270 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8272 getRenderer : function(col){
8273 if(!this.config[col].renderer){
8274 return Roo.grid.ColumnModel.defaultRenderer;
8276 return this.config[col].renderer;
8280 * Sets the rendering (formatting) function for a column.
8281 * @param {Number} col The column index
8282 * @param {Function} fn The function to use to process the cell's raw data
8283 * to return HTML markup for the grid view. The render function is called with
8284 * the following parameters:<ul>
8285 * <li>Data value.</li>
8286 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8287 * <li>css A CSS style string to apply to the table cell.</li>
8288 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8289 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8290 * <li>Row index</li>
8291 * <li>Column index</li>
8292 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8294 setRenderer : function(col, fn){
8295 this.config[col].renderer = fn;
8299 * Returns the width for the specified column.
8300 * @param {Number} col The column index
8301 * @param (optional) {String} gridSize bootstrap width size.
8304 getColumnWidth : function(col, gridSize)
8306 var cfg = this.config[col];
8308 if (typeof(gridSize) == 'undefined') {
8309 return cfg.width * 1 || this.defaultWidth;
8311 if (gridSize === false) { // if we set it..
8312 return cfg.width || false;
8314 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8316 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8317 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8320 return cfg[ sizes[i] ];
8327 * Sets the width for a column.
8328 * @param {Number} col The column index
8329 * @param {Number} width The new width
8331 setColumnWidth : function(col, width, suppressEvent){
8332 this.config[col].width = width;
8333 this.totalWidth = null;
8335 this.fireEvent("widthchange", this, col, width);
8340 * Returns the total width of all columns.
8341 * @param {Boolean} includeHidden True to include hidden column widths
8344 getTotalWidth : function(includeHidden){
8345 if(!this.totalWidth){
8346 this.totalWidth = 0;
8347 for(var i = 0, len = this.config.length; i < len; i++){
8348 if(includeHidden || !this.isHidden(i)){
8349 this.totalWidth += this.getColumnWidth(i);
8353 return this.totalWidth;
8357 * Returns the header for the specified column.
8358 * @param {Number} col The column index
8361 getColumnHeader : function(col){
8362 return this.config[col].header;
8366 * Sets the header for a column.
8367 * @param {Number} col The column index
8368 * @param {String} header The new header
8370 setColumnHeader : function(col, header){
8371 this.config[col].header = header;
8372 this.fireEvent("headerchange", this, col, header);
8376 * Returns the tooltip for the specified column.
8377 * @param {Number} col The column index
8380 getColumnTooltip : function(col){
8381 return this.config[col].tooltip;
8384 * Sets the tooltip for a column.
8385 * @param {Number} col The column index
8386 * @param {String} tooltip The new tooltip
8388 setColumnTooltip : function(col, tooltip){
8389 this.config[col].tooltip = tooltip;
8393 * Returns the dataIndex for the specified column.
8394 * @param {Number} col The column index
8397 getDataIndex : function(col){
8398 return this.config[col].dataIndex;
8402 * Sets the dataIndex for a column.
8403 * @param {Number} col The column index
8404 * @param {Number} dataIndex The new dataIndex
8406 setDataIndex : function(col, dataIndex){
8407 this.config[col].dataIndex = dataIndex;
8413 * Returns true if the cell is editable.
8414 * @param {Number} colIndex The column index
8415 * @param {Number} rowIndex The row index - this is nto actually used..?
8418 isCellEditable : function(colIndex, rowIndex){
8419 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8423 * Returns the editor defined for the cell/column.
8424 * return false or null to disable editing.
8425 * @param {Number} colIndex The column index
8426 * @param {Number} rowIndex The row index
8429 getCellEditor : function(colIndex, rowIndex){
8430 return this.config[colIndex].editor;
8434 * Sets if a column is editable.
8435 * @param {Number} col The column index
8436 * @param {Boolean} editable True if the column is editable
8438 setEditable : function(col, editable){
8439 this.config[col].editable = editable;
8444 * Returns true if the column is hidden.
8445 * @param {Number} colIndex The column index
8448 isHidden : function(colIndex){
8449 return this.config[colIndex].hidden;
8454 * Returns true if the column width cannot be changed
8456 isFixed : function(colIndex){
8457 return this.config[colIndex].fixed;
8461 * Returns true if the column can be resized
8464 isResizable : function(colIndex){
8465 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8468 * Sets if a column is hidden.
8469 * @param {Number} colIndex The column index
8470 * @param {Boolean} hidden True if the column is hidden
8472 setHidden : function(colIndex, hidden){
8473 this.config[colIndex].hidden = hidden;
8474 this.totalWidth = null;
8475 this.fireEvent("hiddenchange", this, colIndex, hidden);
8479 * Sets the editor for a column.
8480 * @param {Number} col The column index
8481 * @param {Object} editor The editor object
8483 setEditor : function(col, editor){
8484 this.config[col].editor = editor;
8487 * Add a column (experimental...) - defaults to adding to the end..
8488 * @param {Object} config
8490 addColumn : function(c)
8493 var i = this.config.length;
8496 if(typeof c.dataIndex == "undefined"){
8499 if(typeof c.renderer == "string"){
8500 c.renderer = Roo.util.Format[c.renderer];
8502 if(typeof c.id == "undefined"){
8505 if(c.editor && c.editor.xtype){
8506 c.editor = Roo.factory(c.editor, Roo.grid);
8508 if(c.editor && c.editor.isFormField){
8509 c.editor = new Roo.grid.GridEditor(c.editor);
8511 this.lookup[c.id] = c;
8516 Roo.grid.ColumnModel.defaultRenderer = function(value)
8518 if(typeof value == "object") {
8521 if(typeof value == "string" && value.length < 1){
8525 return String.format("{0}", value);
8528 // Alias for backwards compatibility
8529 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8532 * Ext JS Library 1.1.1
8533 * Copyright(c) 2006-2007, Ext JS, LLC.
8535 * Originally Released Under LGPL - original licence link has changed is not relivant.
8538 * <script type="text/javascript">
8542 * @class Roo.LoadMask
8543 * A simple utility class for generically masking elements while loading data. If the element being masked has
8544 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8545 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8546 * element's UpdateManager load indicator and will be destroyed after the initial load.
8548 * Create a new LoadMask
8549 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8550 * @param {Object} config The config object
8552 Roo.LoadMask = function(el, config){
8553 this.el = Roo.get(el);
8554 Roo.apply(this, config);
8556 this.store.on('beforeload', this.onBeforeLoad, this);
8557 this.store.on('load', this.onLoad, this);
8558 this.store.on('loadexception', this.onLoadException, this);
8559 this.removeMask = false;
8561 var um = this.el.getUpdateManager();
8562 um.showLoadIndicator = false; // disable the default indicator
8563 um.on('beforeupdate', this.onBeforeLoad, this);
8564 um.on('update', this.onLoad, this);
8565 um.on('failure', this.onLoad, this);
8566 this.removeMask = true;
8570 Roo.LoadMask.prototype = {
8572 * @cfg {Boolean} removeMask
8573 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8574 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8579 * The text to display in a centered loading message box (defaults to 'Loading...')
8583 * @cfg {String} msgCls
8584 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8586 msgCls : 'x-mask-loading',
8589 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8595 * Disables the mask to prevent it from being displayed
8597 disable : function(){
8598 this.disabled = true;
8602 * Enables the mask so that it can be displayed
8604 enable : function(){
8605 this.disabled = false;
8608 onLoadException : function()
8612 if (typeof(arguments[3]) != 'undefined') {
8613 Roo.MessageBox.alert("Error loading",arguments[3]);
8617 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8618 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8625 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8630 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8634 onBeforeLoad : function(){
8636 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8641 destroy : function(){
8643 this.store.un('beforeload', this.onBeforeLoad, this);
8644 this.store.un('load', this.onLoad, this);
8645 this.store.un('loadexception', this.onLoadException, this);
8647 var um = this.el.getUpdateManager();
8648 um.un('beforeupdate', this.onBeforeLoad, this);
8649 um.un('update', this.onLoad, this);
8650 um.un('failure', this.onLoad, this);
8654 * @class Roo.bootstrap.Table
8656 * @extends Roo.bootstrap.Component
8657 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8658 * Similar to Roo.grid.Grid
8660 var table = Roo.factory({
8662 xns : Roo.bootstrap,
8663 autoSizeColumns: true,
8670 sortInfo : { direction : 'ASC', field: 'name' },
8672 xtype : 'HttpProxy',
8675 url : 'https://example.com/some.data.url.json'
8678 xtype : 'JsonReader',
8680 fields : [ 'id', 'name', whatever' ],
8687 xtype : 'ColumnModel',
8691 dataIndex : 'is_in_group',
8694 renderer : function(v, x , r) {
8696 return String.format("{0}", v)
8702 xtype : 'RowSelectionModel',
8703 xns : Roo.bootstrap.Table
8704 // you can add listeners to catch selection change here....
8710 grid.render(Roo.get("some-div"));
8713 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8718 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8719 * @cfg {Roo.data.Store} store The data store to use
8720 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8722 * @cfg {String} cls table class
8725 * @cfg {boolean} striped Should the rows be alternative striped
8726 * @cfg {boolean} bordered Add borders to the table
8727 * @cfg {boolean} hover Add hover highlighting
8728 * @cfg {boolean} condensed Format condensed
8729 * @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,
8730 * also adds table-responsive (see bootstrap docs for details)
8731 * @cfg {Boolean} loadMask (true|false) default false
8732 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8733 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8734 * @cfg {Boolean} rowSelection (true|false) default false
8735 * @cfg {Boolean} cellSelection (true|false) default false
8736 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8737 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8738 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8739 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8740 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8741 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8744 * Create a new Table
8745 * @param {Object} config The config object
8748 Roo.bootstrap.Table = function(config)
8750 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8753 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8754 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8755 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8756 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8758 this.view = this; // compat with grid.
8760 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8762 this.sm.grid = this;
8763 this.selModel = Roo.factory(this.sm, Roo.grid);
8764 this.sm = this.selModel;
8765 this.sm.xmodule = this.xmodule || false;
8768 if (this.cm && typeof(this.cm.config) == 'undefined') {
8769 this.colModel = new Roo.grid.ColumnModel(this.cm);
8770 this.cm = this.colModel;
8771 this.cm.xmodule = this.xmodule || false;
8774 this.store= Roo.factory(this.store, Roo.data);
8775 this.ds = this.store;
8776 this.ds.xmodule = this.xmodule || false;
8779 if (this.footer && this.store) {
8780 this.footer.dataSource = this.ds;
8781 this.footer = Roo.factory(this.footer);
8788 * Fires when a cell is clicked
8789 * @param {Roo.bootstrap.Table} this
8790 * @param {Roo.Element} el
8791 * @param {Number} rowIndex
8792 * @param {Number} columnIndex
8793 * @param {Roo.EventObject} e
8797 * @event celldblclick
8798 * Fires when a cell is double clicked
8799 * @param {Roo.bootstrap.Table} this
8800 * @param {Roo.Element} el
8801 * @param {Number} rowIndex
8802 * @param {Number} columnIndex
8803 * @param {Roo.EventObject} e
8805 "celldblclick" : true,
8808 * Fires when a row is clicked
8809 * @param {Roo.bootstrap.Table} this
8810 * @param {Roo.Element} el
8811 * @param {Number} rowIndex
8812 * @param {Roo.EventObject} e
8816 * @event rowdblclick
8817 * Fires when a row is double clicked
8818 * @param {Roo.bootstrap.Table} this
8819 * @param {Roo.Element} el
8820 * @param {Number} rowIndex
8821 * @param {Roo.EventObject} e
8823 "rowdblclick" : true,
8826 * Fires when a mouseover occur
8827 * @param {Roo.bootstrap.Table} this
8828 * @param {Roo.Element} el
8829 * @param {Number} rowIndex
8830 * @param {Number} columnIndex
8831 * @param {Roo.EventObject} e
8836 * Fires when a mouseout occur
8837 * @param {Roo.bootstrap.Table} this
8838 * @param {Roo.Element} el
8839 * @param {Number} rowIndex
8840 * @param {Number} columnIndex
8841 * @param {Roo.EventObject} e
8846 * Fires when a row is rendered, so you can change add a style to it.
8847 * @param {Roo.bootstrap.Table} this
8848 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8852 * @event rowsrendered
8853 * Fires when all the rows have been rendered
8854 * @param {Roo.bootstrap.Table} this
8856 'rowsrendered' : true,
8858 * @event contextmenu
8859 * The raw contextmenu event for the entire grid.
8860 * @param {Roo.EventObject} e
8862 "contextmenu" : true,
8864 * @event rowcontextmenu
8865 * Fires when a row is right clicked
8866 * @param {Roo.bootstrap.Table} this
8867 * @param {Number} rowIndex
8868 * @param {Roo.EventObject} e
8870 "rowcontextmenu" : true,
8872 * @event cellcontextmenu
8873 * Fires when a cell is right clicked
8874 * @param {Roo.bootstrap.Table} this
8875 * @param {Number} rowIndex
8876 * @param {Number} cellIndex
8877 * @param {Roo.EventObject} e
8879 "cellcontextmenu" : true,
8881 * @event headercontextmenu
8882 * Fires when a header is right clicked
8883 * @param {Roo.bootstrap.Table} this
8884 * @param {Number} columnIndex
8885 * @param {Roo.EventObject} e
8887 "headercontextmenu" : true,
8890 * The raw mousedown event for the entire grid.
8891 * @param {Roo.EventObject} e
8898 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8914 enableColumnResize: true,
8916 rowSelection : false,
8917 cellSelection : false,
8920 minColumnWidth : 50,
8922 // Roo.Element - the tbody
8923 bodyEl: false, // <tbody> Roo.Element - thead element
8924 headEl: false, // <thead> Roo.Element - thead element
8925 resizeProxy : false, // proxy element for dragging?
8929 container: false, // used by gridpanel...
8935 auto_hide_footer : false,
8937 view: false, // actually points to this..
8939 getAutoCreate : function()
8941 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8948 // this get's auto added by panel.Grid
8949 if (this.scrollBody) {
8950 cfg.cls += ' table-body-fixed';
8953 cfg.cls += ' table-striped';
8957 cfg.cls += ' table-hover';
8959 if (this.bordered) {
8960 cfg.cls += ' table-bordered';
8962 if (this.condensed) {
8963 cfg.cls += ' table-condensed';
8966 if (this.responsive) {
8967 cfg.cls += ' table-responsive';
8971 cfg.cls+= ' ' +this.cls;
8977 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8980 if(this.store || this.cm){
8981 if(this.headerShow){
8982 cfg.cn.push(this.renderHeader());
8985 cfg.cn.push(this.renderBody());
8987 if(this.footerShow){
8988 cfg.cn.push(this.renderFooter());
8990 // where does this come from?
8991 //cfg.cls+= ' TableGrid';
8994 return { cn : [ cfg ] };
8997 initEvents : function()
8999 if(!this.store || !this.cm){
9002 if (this.selModel) {
9003 this.selModel.initEvents();
9007 //Roo.log('initEvents with ds!!!!');
9009 this.bodyEl = this.el.select('tbody', true).first();
9010 this.headEl = this.el.select('thead', true).first();
9011 this.mainFoot = this.el.select('tfoot', true).first();
9016 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9017 e.on('click', this.sort, this);
9021 // why is this done????? = it breaks dialogs??
9022 //this.parent().el.setStyle('position', 'relative');
9026 this.footer.parentId = this.id;
9027 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9030 this.el.select('tfoot tr td').first().addClass('hide');
9035 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9038 this.store.on('load', this.onLoad, this);
9039 this.store.on('beforeload', this.onBeforeLoad, this);
9040 this.store.on('update', this.onUpdate, this);
9041 this.store.on('add', this.onAdd, this);
9042 this.store.on("clear", this.clear, this);
9044 this.el.on("contextmenu", this.onContextMenu, this);
9047 this.cm.on("headerchange", this.onHeaderChange, this);
9048 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9050 //?? does bodyEl get replaced on render?
9051 this.bodyEl.on("click", this.onClick, this);
9052 this.bodyEl.on("dblclick", this.onDblClick, this);
9053 this.bodyEl.on('scroll', this.onBodyScroll, this);
9055 // guessing mainbody will work - this relays usually caught by selmodel at present.
9056 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9059 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9062 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9063 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9068 // Compatibility with grid - we implement all the view features at present.
9069 getView : function()
9074 initCSS : function()
9078 var cm = this.cm, styles = [];
9079 this.CSS.removeStyleSheet(this.id + '-cssrules');
9080 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9081 // we can honour xs/sm/md/xl as widths...
9082 // we first have to decide what widht we are currently at...
9083 var sz = Roo.getGridSize();
9087 var cols = []; // visable cols.
9089 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9090 var w = cm.getColumnWidth(i, false);
9092 cols.push( { rel : false, abs : 0 });
9096 cols.push( { rel : false, abs : w });
9098 last = i; // not really..
9101 var w = cm.getColumnWidth(i, sz);
9106 cols.push( { rel : w, abs : false });
9109 var avail = this.bodyEl.dom.clientWidth - total_abs;
9111 var unitWidth = Math.floor(avail / total);
9112 var rem = avail - (unitWidth * total);
9114 var hidden, width, pos = 0 , splithide , left;
9115 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9117 hidden = 'display:none;';
9119 width = 'width:0px;';
9121 if(!cm.isHidden(i)){
9125 // we can honour xs/sm/md/xl ?
9126 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9128 hidden = 'display:none;';
9130 // width should return a small number...
9132 w+=rem; // add the remaining with..
9135 left = "left:" + (pos -4) + "px;";
9136 width = "width:" + w+ "px;";
9139 if (this.responsive) {
9142 hidden = cm.isHidden(i) ? 'display:none;' : '';
9143 splithide = 'display: none;';
9146 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9149 splithide = 'display:none;';
9152 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9153 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9158 //Roo.log(styles.join(''));
9159 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9165 onContextMenu : function(e, t)
9167 this.processEvent("contextmenu", e);
9170 processEvent : function(name, e)
9172 if (name != 'touchstart' ) {
9173 this.fireEvent(name, e);
9176 var t = e.getTarget();
9178 var cell = Roo.get(t);
9184 if(cell.findParent('tfoot', false, true)){
9188 if(cell.findParent('thead', false, true)){
9190 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9191 cell = Roo.get(t).findParent('th', false, true);
9193 Roo.log("failed to find th in thead?");
9194 Roo.log(e.getTarget());
9199 var cellIndex = cell.dom.cellIndex;
9201 var ename = name == 'touchstart' ? 'click' : name;
9202 this.fireEvent("header" + ename, this, cellIndex, e);
9207 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9208 cell = Roo.get(t).findParent('td', false, true);
9210 Roo.log("failed to find th in tbody?");
9211 Roo.log(e.getTarget());
9216 var row = cell.findParent('tr', false, true);
9217 var cellIndex = cell.dom.cellIndex;
9218 var rowIndex = row.dom.rowIndex - 1;
9222 this.fireEvent("row" + name, this, rowIndex, e);
9226 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9232 onMouseover : function(e, el)
9234 var cell = Roo.get(el);
9240 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9241 cell = cell.findParent('td', false, true);
9244 var row = cell.findParent('tr', false, true);
9245 var cellIndex = cell.dom.cellIndex;
9246 var rowIndex = row.dom.rowIndex - 1; // start from 0
9248 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9252 onMouseout : function(e, el)
9254 var cell = Roo.get(el);
9260 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9261 cell = cell.findParent('td', false, true);
9264 var row = cell.findParent('tr', false, true);
9265 var cellIndex = cell.dom.cellIndex;
9266 var rowIndex = row.dom.rowIndex - 1; // start from 0
9268 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9272 onClick : function(e, el)
9274 var cell = Roo.get(el);
9276 if(!cell || (!this.cellSelection && !this.rowSelection)){
9280 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9281 cell = cell.findParent('td', false, true);
9284 if(!cell || typeof(cell) == 'undefined'){
9288 var row = cell.findParent('tr', false, true);
9290 if(!row || typeof(row) == 'undefined'){
9294 var cellIndex = cell.dom.cellIndex;
9295 var rowIndex = this.getRowIndex(row);
9297 // why??? - should these not be based on SelectionModel?
9298 //if(this.cellSelection){
9299 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9302 //if(this.rowSelection){
9303 this.fireEvent('rowclick', this, row, rowIndex, e);
9308 onDblClick : function(e,el)
9310 var cell = Roo.get(el);
9312 if(!cell || (!this.cellSelection && !this.rowSelection)){
9316 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9317 cell = cell.findParent('td', false, true);
9320 if(!cell || typeof(cell) == 'undefined'){
9324 var row = cell.findParent('tr', false, true);
9326 if(!row || typeof(row) == 'undefined'){
9330 var cellIndex = cell.dom.cellIndex;
9331 var rowIndex = this.getRowIndex(row);
9333 if(this.cellSelection){
9334 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9337 if(this.rowSelection){
9338 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9341 findRowIndex : function(el)
9343 var cell = Roo.get(el);
9347 var row = cell.findParent('tr', false, true);
9349 if(!row || typeof(row) == 'undefined'){
9352 return this.getRowIndex(row);
9354 sort : function(e,el)
9356 var col = Roo.get(el);
9358 if(!col.hasClass('sortable')){
9362 var sort = col.attr('sort');
9365 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9369 this.store.sortInfo = {field : sort, direction : dir};
9372 Roo.log("calling footer first");
9373 this.footer.onClick('first');
9376 this.store.load({ params : { start : 0 } });
9380 renderHeader : function()
9388 this.totalWidth = 0;
9390 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9392 var config = cm.config[i];
9396 cls : 'x-hcol-' + i,
9399 html: cm.getColumnHeader(i)
9402 var tooltip = cm.getColumnTooltip(i);
9404 c.tooltip = tooltip;
9410 if(typeof(config.sortable) != 'undefined' && config.sortable){
9411 c.cls += ' sortable';
9412 c.html = '<i class="fa"></i>' + c.html;
9415 // could use BS4 hidden-..-down
9417 if(typeof(config.lgHeader) != 'undefined'){
9418 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9421 if(typeof(config.mdHeader) != 'undefined'){
9422 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9425 if(typeof(config.smHeader) != 'undefined'){
9426 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9429 if(typeof(config.xsHeader) != 'undefined'){
9430 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9437 if(typeof(config.tooltip) != 'undefined'){
9438 c.tooltip = config.tooltip;
9441 if(typeof(config.colspan) != 'undefined'){
9442 c.colspan = config.colspan;
9445 // hidden is handled by CSS now
9447 if(typeof(config.dataIndex) != 'undefined'){
9448 c.sort = config.dataIndex;
9453 if(typeof(config.align) != 'undefined' && config.align.length){
9454 c.style += ' text-align:' + config.align + ';';
9457 /* width is done in CSS
9458 *if(typeof(config.width) != 'undefined'){
9459 c.style += ' width:' + config.width + 'px;';
9460 this.totalWidth += config.width;
9462 this.totalWidth += 100; // assume minimum of 100 per column?
9466 if(typeof(config.cls) != 'undefined'){
9467 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9469 // this is the bit that doesnt reall work at all...
9471 if (this.responsive) {
9474 ['xs','sm','md','lg'].map(function(size){
9476 if(typeof(config[size]) == 'undefined'){
9480 if (!config[size]) { // 0 = hidden
9481 // BS 4 '0' is treated as hide that column and below.
9482 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9486 c.cls += ' col-' + size + '-' + config[size] + (
9487 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9495 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9506 renderBody : function()
9516 colspan : this.cm.getColumnCount()
9526 renderFooter : function()
9536 colspan : this.cm.getColumnCount()
9550 // Roo.log('ds onload');
9555 var ds = this.store;
9557 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9558 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9559 if (_this.store.sortInfo) {
9561 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9562 e.select('i', true).addClass(['fa-arrow-up']);
9565 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9566 e.select('i', true).addClass(['fa-arrow-down']);
9571 var tbody = this.bodyEl;
9573 if(ds.getCount() > 0){
9574 ds.data.each(function(d,rowIndex){
9575 var row = this.renderRow(cm, ds, rowIndex);
9577 tbody.createChild(row);
9581 if(row.cellObjects.length){
9582 Roo.each(row.cellObjects, function(r){
9583 _this.renderCellObject(r);
9590 var tfoot = this.el.select('tfoot', true).first();
9592 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9594 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9596 var total = this.ds.getTotalCount();
9598 if(this.footer.pageSize < total){
9599 this.mainFoot.show();
9603 Roo.each(this.el.select('tbody td', true).elements, function(e){
9604 e.on('mouseover', _this.onMouseover, _this);
9607 Roo.each(this.el.select('tbody td', true).elements, function(e){
9608 e.on('mouseout', _this.onMouseout, _this);
9610 this.fireEvent('rowsrendered', this);
9614 this.initCSS(); /// resize cols
9620 onUpdate : function(ds,record)
9622 this.refreshRow(record);
9626 onRemove : function(ds, record, index, isUpdate){
9627 if(isUpdate !== true){
9628 this.fireEvent("beforerowremoved", this, index, record);
9630 var bt = this.bodyEl.dom;
9632 var rows = this.el.select('tbody > tr', true).elements;
9634 if(typeof(rows[index]) != 'undefined'){
9635 bt.removeChild(rows[index].dom);
9638 // if(bt.rows[index]){
9639 // bt.removeChild(bt.rows[index]);
9642 if(isUpdate !== true){
9643 //this.stripeRows(index);
9644 //this.syncRowHeights(index, index);
9646 this.fireEvent("rowremoved", this, index, record);
9650 onAdd : function(ds, records, rowIndex)
9652 //Roo.log('on Add called');
9653 // - note this does not handle multiple adding very well..
9654 var bt = this.bodyEl.dom;
9655 for (var i =0 ; i < records.length;i++) {
9656 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9657 //Roo.log(records[i]);
9658 //Roo.log(this.store.getAt(rowIndex+i));
9659 this.insertRow(this.store, rowIndex + i, false);
9666 refreshRow : function(record){
9667 var ds = this.store, index;
9668 if(typeof record == 'number'){
9670 record = ds.getAt(index);
9672 index = ds.indexOf(record);
9674 return; // should not happen - but seems to
9677 this.insertRow(ds, index, true);
9679 this.onRemove(ds, record, index+1, true);
9681 //this.syncRowHeights(index, index);
9683 this.fireEvent("rowupdated", this, index, record);
9685 // private - called by RowSelection
9686 onRowSelect : function(rowIndex){
9687 var row = this.getRowDom(rowIndex);
9688 row.addClass(['bg-info','info']);
9690 // private - called by RowSelection
9691 onRowDeselect : function(rowIndex)
9696 var row = this.getRowDom(rowIndex);
9697 row.removeClass(['bg-info','info']);
9700 * Focuses the specified row.
9701 * @param {Number} row The row index
9703 focusRow : function(row)
9705 //Roo.log('GridView.focusRow');
9706 var x = this.bodyEl.dom.scrollLeft;
9707 this.focusCell(row, 0, false);
9708 this.bodyEl.dom.scrollLeft = x;
9712 * Focuses the specified cell.
9713 * @param {Number} row The row index
9714 * @param {Number} col The column index
9715 * @param {Boolean} hscroll false to disable horizontal scrolling
9717 focusCell : function(row, col, hscroll)
9719 //Roo.log('GridView.focusCell');
9720 var el = this.ensureVisible(row, col, hscroll);
9721 // not sure what focusEL achives = it's a <a> pos relative
9722 //this.focusEl.alignTo(el, "tl-tl");
9724 // this.focusEl.focus();
9726 // this.focusEl.focus.defer(1, this.focusEl);
9731 * Scrolls the specified cell into view
9732 * @param {Number} row The row index
9733 * @param {Number} col The column index
9734 * @param {Boolean} hscroll false to disable horizontal scrolling
9736 ensureVisible : function(row, col, hscroll)
9738 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9739 //return null; //disable for testing.
9740 if(typeof row != "number"){
9743 if(row < 0 && row >= this.ds.getCount()){
9746 col = (col !== undefined ? col : 0);
9748 while(cm.isHidden(col)){
9752 var el = this.getCellDom(row, col);
9756 var c = this.bodyEl.dom;
9758 var ctop = parseInt(el.offsetTop, 10);
9759 var cleft = parseInt(el.offsetLeft, 10);
9760 var cbot = ctop + el.offsetHeight;
9761 var cright = cleft + el.offsetWidth;
9763 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9764 var ch = 0; //?? header is not withing the area?
9765 var stop = parseInt(c.scrollTop, 10);
9766 var sleft = parseInt(c.scrollLeft, 10);
9767 var sbot = stop + ch;
9768 var sright = sleft + c.clientWidth;
9770 Roo.log('GridView.ensureVisible:' +
9772 ' c.clientHeight:' + c.clientHeight +
9773 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9782 //Roo.log("set scrolltop to ctop DISABLE?");
9783 }else if(cbot > sbot){
9784 //Roo.log("set scrolltop to cbot-ch");
9785 c.scrollTop = cbot-ch;
9788 if(hscroll !== false){
9790 c.scrollLeft = cleft;
9791 }else if(cright > sright){
9792 c.scrollLeft = cright-c.clientWidth;
9800 insertRow : function(dm, rowIndex, isUpdate){
9803 this.fireEvent("beforerowsinserted", this, rowIndex);
9805 //var s = this.getScrollState();
9806 var row = this.renderRow(this.cm, this.store, rowIndex);
9807 // insert before rowIndex..
9808 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9812 if(row.cellObjects.length){
9813 Roo.each(row.cellObjects, function(r){
9814 _this.renderCellObject(r);
9819 this.fireEvent("rowsinserted", this, rowIndex);
9820 //this.syncRowHeights(firstRow, lastRow);
9821 //this.stripeRows(firstRow);
9828 getRowDom : function(rowIndex)
9830 var rows = this.el.select('tbody > tr', true).elements;
9832 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9835 getCellDom : function(rowIndex, colIndex)
9837 var row = this.getRowDom(rowIndex);
9838 if (row === false) {
9841 var cols = row.select('td', true).elements;
9842 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9846 // returns the object tree for a tr..
9849 renderRow : function(cm, ds, rowIndex)
9851 var d = ds.getAt(rowIndex);
9855 cls : 'x-row-' + rowIndex,
9859 var cellObjects = [];
9861 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9862 var config = cm.config[i];
9864 var renderer = cm.getRenderer(i);
9868 if(typeof(renderer) !== 'undefined'){
9869 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9871 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9872 // and are rendered into the cells after the row is rendered - using the id for the element.
9874 if(typeof(value) === 'object'){
9884 rowIndex : rowIndex,
9889 this.fireEvent('rowclass', this, rowcfg);
9893 // this might end up displaying HTML?
9894 // this is too messy... - better to only do it on columsn you know are going to be too long
9895 //tooltip : (typeof(value) === 'object') ? '' : value,
9896 cls : rowcfg.rowClass + ' x-col-' + i,
9898 html: (typeof(value) === 'object') ? '' : value
9905 if(typeof(config.colspan) != 'undefined'){
9906 td.colspan = config.colspan;
9911 if(typeof(config.align) != 'undefined' && config.align.length){
9912 td.style += ' text-align:' + config.align + ';';
9914 if(typeof(config.valign) != 'undefined' && config.valign.length){
9915 td.style += ' vertical-align:' + config.valign + ';';
9918 if(typeof(config.width) != 'undefined'){
9919 td.style += ' width:' + config.width + 'px;';
9923 if(typeof(config.cursor) != 'undefined'){
9924 td.style += ' cursor:' + config.cursor + ';';
9927 if(typeof(config.cls) != 'undefined'){
9928 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9930 if (this.responsive) {
9931 ['xs','sm','md','lg'].map(function(size){
9933 if(typeof(config[size]) == 'undefined'){
9939 if (!config[size]) { // 0 = hidden
9940 // BS 4 '0' is treated as hide that column and below.
9941 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9945 td.cls += ' col-' + size + '-' + config[size] + (
9946 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9956 row.cellObjects = cellObjects;
9964 onBeforeLoad : function()
9973 this.el.select('tbody', true).first().dom.innerHTML = '';
9976 * Show or hide a row.
9977 * @param {Number} rowIndex to show or hide
9978 * @param {Boolean} state hide
9980 setRowVisibility : function(rowIndex, state)
9982 var bt = this.bodyEl.dom;
9984 var rows = this.el.select('tbody > tr', true).elements;
9986 if(typeof(rows[rowIndex]) == 'undefined'){
9989 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9994 getSelectionModel : function(){
9996 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9998 return this.selModel;
10001 * Render the Roo.bootstrap object from renderder
10003 renderCellObject : function(r)
10007 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10009 var t = r.cfg.render(r.container);
10012 Roo.each(r.cfg.cn, function(c){
10014 container: t.getChildContainer(),
10017 _this.renderCellObject(child);
10022 * get the Row Index from a dom element.
10023 * @param {Roo.Element} row The row to look for
10024 * @returns {Number} the row
10026 getRowIndex : function(row)
10030 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10041 * get the header TH element for columnIndex
10042 * @param {Number} columnIndex
10043 * @returns {Roo.Element}
10045 getHeaderIndex: function(colIndex)
10047 var cols = this.headEl.select('th', true).elements;
10048 return cols[colIndex];
10051 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10052 * @param {domElement} cell to look for
10053 * @returns {Number} the column
10055 getCellIndex : function(cell)
10057 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10059 return parseInt(id[1], 10);
10064 * Returns the grid's underlying element = used by panel.Grid
10065 * @return {Element} The element
10067 getGridEl : function(){
10071 * Forces a resize - used by panel.Grid
10072 * @return {Element} The element
10074 autoSize : function()
10076 //var ctr = Roo.get(this.container.dom.parentElement);
10077 var ctr = Roo.get(this.el.dom);
10079 var thd = this.getGridEl().select('thead',true).first();
10080 var tbd = this.getGridEl().select('tbody', true).first();
10081 var tfd = this.getGridEl().select('tfoot', true).first();
10083 var cw = ctr.getWidth();
10084 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10088 tbd.setWidth(ctr.getWidth());
10089 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10090 // this needs fixing for various usage - currently only hydra job advers I think..
10092 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10094 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10097 cw = Math.max(cw, this.totalWidth);
10098 this.getGridEl().select('tbody tr',true).setWidth(cw);
10101 // resize 'expandable coloumn?
10103 return; // we doe not have a view in this design..
10106 onBodyScroll: function()
10108 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10110 this.headEl.setStyle({
10111 'position' : 'relative',
10112 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10118 var scrollHeight = this.bodyEl.dom.scrollHeight;
10120 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10122 var height = this.bodyEl.getHeight();
10124 if(scrollHeight - height == scrollTop) {
10126 var total = this.ds.getTotalCount();
10128 if(this.footer.cursor + this.footer.pageSize < total){
10130 this.footer.ds.load({
10132 start : this.footer.cursor + this.footer.pageSize,
10133 limit : this.footer.pageSize
10142 onColumnSplitterMoved : function(i, diff)
10144 this.userResized = true;
10146 var cm = this.colModel;
10148 var w = this.getHeaderIndex(i).getWidth() + diff;
10151 cm.setColumnWidth(i, w, true);
10153 //var cid = cm.getColumnId(i); << not used in this version?
10154 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10156 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10157 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10158 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10160 //this.updateSplitters();
10161 //this.layout(); << ??
10162 this.fireEvent("columnresize", i, w);
10164 onHeaderChange : function()
10166 var header = this.renderHeader();
10167 var table = this.el.select('table', true).first();
10169 this.headEl.remove();
10170 this.headEl = table.createChild(header, this.bodyEl, false);
10172 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10173 e.on('click', this.sort, this);
10176 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10177 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10182 onHiddenChange : function(colModel, colIndex, hidden)
10185 this.cm.setHidden()
10186 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10187 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10189 this.CSS.updateRule(thSelector, "display", "");
10190 this.CSS.updateRule(tdSelector, "display", "");
10193 this.CSS.updateRule(thSelector, "display", "none");
10194 this.CSS.updateRule(tdSelector, "display", "none");
10197 // onload calls initCSS()
10198 this.onHeaderChange();
10202 setColumnWidth: function(col_index, width)
10204 // width = "md-2 xs-2..."
10205 if(!this.colModel.config[col_index]) {
10209 var w = width.split(" ");
10211 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10213 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10216 for(var j = 0; j < w.length; j++) {
10222 var size_cls = w[j].split("-");
10224 if(!Number.isInteger(size_cls[1] * 1)) {
10228 if(!this.colModel.config[col_index][size_cls[0]]) {
10232 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10236 h_row[0].classList.replace(
10237 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10238 "col-"+size_cls[0]+"-"+size_cls[1]
10241 for(var i = 0; i < rows.length; i++) {
10243 var size_cls = w[j].split("-");
10245 if(!Number.isInteger(size_cls[1] * 1)) {
10249 if(!this.colModel.config[col_index][size_cls[0]]) {
10253 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10257 rows[i].classList.replace(
10258 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10259 "col-"+size_cls[0]+"-"+size_cls[1]
10263 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10268 // currently only used to find the split on drag..
10269 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10274 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10275 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10284 * @class Roo.bootstrap.TableCell
10285 * @extends Roo.bootstrap.Component
10286 * Bootstrap TableCell class
10287 * @cfg {String} html cell contain text
10288 * @cfg {String} cls cell class
10289 * @cfg {String} tag cell tag (td|th) default td
10290 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10291 * @cfg {String} align Aligns the content in a cell
10292 * @cfg {String} axis Categorizes cells
10293 * @cfg {String} bgcolor Specifies the background color of a cell
10294 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10295 * @cfg {Number} colspan Specifies the number of columns a cell should span
10296 * @cfg {String} headers Specifies one or more header cells a cell is related to
10297 * @cfg {Number} height Sets the height of a cell
10298 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10299 * @cfg {Number} rowspan Sets the number of rows a cell should span
10300 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10301 * @cfg {String} valign Vertical aligns the content in a cell
10302 * @cfg {Number} width Specifies the width of a cell
10305 * Create a new TableCell
10306 * @param {Object} config The config object
10309 Roo.bootstrap.TableCell = function(config){
10310 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10313 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10333 getAutoCreate : function(){
10334 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10341 cfg.tag = this.tag;
10354 cfg.align=this.align
10359 if (this.bgcolor) {
10360 cfg.bgcolor=this.bgcolor
10362 if (this.charoff) {
10363 cfg.charoff=this.charoff
10365 if (this.colspan) {
10366 cfg.colspan=this.colspan
10368 if (this.headers) {
10369 cfg.headers=this.headers
10372 cfg.height=this.height
10375 cfg.nowrap=this.nowrap
10377 if (this.rowspan) {
10378 cfg.rowspan=this.rowspan
10381 cfg.scope=this.scope
10384 cfg.valign=this.valign
10387 cfg.width=this.width
10406 * @class Roo.bootstrap.TableRow
10407 * @extends Roo.bootstrap.Component
10408 * Bootstrap TableRow class
10409 * @cfg {String} cls row class
10410 * @cfg {String} align Aligns the content in a table row
10411 * @cfg {String} bgcolor Specifies a background color for a table row
10412 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10413 * @cfg {String} valign Vertical aligns the content in a table row
10416 * Create a new TableRow
10417 * @param {Object} config The config object
10420 Roo.bootstrap.TableRow = function(config){
10421 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10424 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10432 getAutoCreate : function(){
10433 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10440 cfg.cls = this.cls;
10443 cfg.align = this.align;
10446 cfg.bgcolor = this.bgcolor;
10449 cfg.charoff = this.charoff;
10452 cfg.valign = this.valign;
10470 * @class Roo.bootstrap.TableBody
10471 * @extends Roo.bootstrap.Component
10472 * Bootstrap TableBody class
10473 * @cfg {String} cls element class
10474 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10475 * @cfg {String} align Aligns the content inside the element
10476 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10477 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10480 * Create a new TableBody
10481 * @param {Object} config The config object
10484 Roo.bootstrap.TableBody = function(config){
10485 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10488 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10496 getAutoCreate : function(){
10497 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10507 cfg.tag = this.tag;
10511 cfg.align = this.align;
10514 cfg.charoff = this.charoff;
10517 cfg.valign = this.valign;
10524 // initEvents : function()
10527 // if(!this.store){
10531 // this.store = Roo.factory(this.store, Roo.data);
10532 // this.store.on('load', this.onLoad, this);
10534 // this.store.load();
10538 // onLoad: function ()
10540 // this.fireEvent('load', this);
10550 * Ext JS Library 1.1.1
10551 * Copyright(c) 2006-2007, Ext JS, LLC.
10553 * Originally Released Under LGPL - original licence link has changed is not relivant.
10556 * <script type="text/javascript">
10559 // as we use this in bootstrap.
10560 Roo.namespace('Roo.form');
10562 * @class Roo.form.Action
10563 * Internal Class used to handle form actions
10565 * @param {Roo.form.BasicForm} el The form element or its id
10566 * @param {Object} config Configuration options
10571 // define the action interface
10572 Roo.form.Action = function(form, options){
10574 this.options = options || {};
10577 * Client Validation Failed
10580 Roo.form.Action.CLIENT_INVALID = 'client';
10582 * Server Validation Failed
10585 Roo.form.Action.SERVER_INVALID = 'server';
10587 * Connect to Server Failed
10590 Roo.form.Action.CONNECT_FAILURE = 'connect';
10592 * Reading Data from Server Failed
10595 Roo.form.Action.LOAD_FAILURE = 'load';
10597 Roo.form.Action.prototype = {
10599 failureType : undefined,
10600 response : undefined,
10601 result : undefined,
10603 // interface method
10604 run : function(options){
10608 // interface method
10609 success : function(response){
10613 // interface method
10614 handleResponse : function(response){
10618 // default connection failure
10619 failure : function(response){
10621 this.response = response;
10622 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10623 this.form.afterAction(this, false);
10626 processResponse : function(response){
10627 this.response = response;
10628 if(!response.responseText){
10631 this.result = this.handleResponse(response);
10632 return this.result;
10635 // utility functions used internally
10636 getUrl : function(appendParams){
10637 var url = this.options.url || this.form.url || this.form.el.dom.action;
10639 var p = this.getParams();
10641 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10647 getMethod : function(){
10648 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10651 getParams : function(){
10652 var bp = this.form.baseParams;
10653 var p = this.options.params;
10655 if(typeof p == "object"){
10656 p = Roo.urlEncode(Roo.applyIf(p, bp));
10657 }else if(typeof p == 'string' && bp){
10658 p += '&' + Roo.urlEncode(bp);
10661 p = Roo.urlEncode(bp);
10666 createCallback : function(){
10668 success: this.success,
10669 failure: this.failure,
10671 timeout: (this.form.timeout*1000),
10672 upload: this.form.fileUpload ? this.success : undefined
10677 Roo.form.Action.Submit = function(form, options){
10678 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10681 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10684 haveProgress : false,
10685 uploadComplete : false,
10687 // uploadProgress indicator.
10688 uploadProgress : function()
10690 if (!this.form.progressUrl) {
10694 if (!this.haveProgress) {
10695 Roo.MessageBox.progress("Uploading", "Uploading");
10697 if (this.uploadComplete) {
10698 Roo.MessageBox.hide();
10702 this.haveProgress = true;
10704 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10706 var c = new Roo.data.Connection();
10708 url : this.form.progressUrl,
10713 success : function(req){
10714 //console.log(data);
10718 rdata = Roo.decode(req.responseText)
10720 Roo.log("Invalid data from server..");
10724 if (!rdata || !rdata.success) {
10726 Roo.MessageBox.alert(Roo.encode(rdata));
10729 var data = rdata.data;
10731 if (this.uploadComplete) {
10732 Roo.MessageBox.hide();
10737 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10738 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10741 this.uploadProgress.defer(2000,this);
10744 failure: function(data) {
10745 Roo.log('progress url failed ');
10756 // run get Values on the form, so it syncs any secondary forms.
10757 this.form.getValues();
10759 var o = this.options;
10760 var method = this.getMethod();
10761 var isPost = method == 'POST';
10762 if(o.clientValidation === false || this.form.isValid()){
10764 if (this.form.progressUrl) {
10765 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10766 (new Date() * 1) + '' + Math.random());
10771 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10772 form:this.form.el.dom,
10773 url:this.getUrl(!isPost),
10775 params:isPost ? this.getParams() : null,
10776 isUpload: this.form.fileUpload,
10777 formData : this.form.formData
10780 this.uploadProgress();
10782 }else if (o.clientValidation !== false){ // client validation failed
10783 this.failureType = Roo.form.Action.CLIENT_INVALID;
10784 this.form.afterAction(this, false);
10788 success : function(response)
10790 this.uploadComplete= true;
10791 if (this.haveProgress) {
10792 Roo.MessageBox.hide();
10796 var result = this.processResponse(response);
10797 if(result === true || result.success){
10798 this.form.afterAction(this, true);
10802 this.form.markInvalid(result.errors);
10803 this.failureType = Roo.form.Action.SERVER_INVALID;
10805 this.form.afterAction(this, false);
10807 failure : function(response)
10809 this.uploadComplete= true;
10810 if (this.haveProgress) {
10811 Roo.MessageBox.hide();
10814 this.response = response;
10815 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10816 this.form.afterAction(this, false);
10819 handleResponse : function(response){
10820 if(this.form.errorReader){
10821 var rs = this.form.errorReader.read(response);
10824 for(var i = 0, len = rs.records.length; i < len; i++) {
10825 var r = rs.records[i];
10826 errors[i] = r.data;
10829 if(errors.length < 1){
10833 success : rs.success,
10839 ret = Roo.decode(response.responseText);
10843 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10853 Roo.form.Action.Load = function(form, options){
10854 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10855 this.reader = this.form.reader;
10858 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10863 Roo.Ajax.request(Roo.apply(
10864 this.createCallback(), {
10865 method:this.getMethod(),
10866 url:this.getUrl(false),
10867 params:this.getParams()
10871 success : function(response){
10873 var result = this.processResponse(response);
10874 if(result === true || !result.success || !result.data){
10875 this.failureType = Roo.form.Action.LOAD_FAILURE;
10876 this.form.afterAction(this, false);
10879 this.form.clearInvalid();
10880 this.form.setValues(result.data);
10881 this.form.afterAction(this, true);
10884 handleResponse : function(response){
10885 if(this.form.reader){
10886 var rs = this.form.reader.read(response);
10887 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10889 success : rs.success,
10893 return Roo.decode(response.responseText);
10897 Roo.form.Action.ACTION_TYPES = {
10898 'load' : Roo.form.Action.Load,
10899 'submit' : Roo.form.Action.Submit
10908 * @class Roo.bootstrap.Form
10909 * @extends Roo.bootstrap.Component
10910 * Bootstrap Form class
10911 * @cfg {String} method GET | POST (default POST)
10912 * @cfg {String} labelAlign top | left (default top)
10913 * @cfg {String} align left | right - for navbars
10914 * @cfg {Boolean} loadMask load mask when submit (default true)
10918 * Create a new Form
10919 * @param {Object} config The config object
10923 Roo.bootstrap.Form = function(config){
10925 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10927 Roo.bootstrap.Form.popover.apply();
10931 * @event clientvalidation
10932 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10933 * @param {Form} this
10934 * @param {Boolean} valid true if the form has passed client-side validation
10936 clientvalidation: true,
10938 * @event beforeaction
10939 * Fires before any action is performed. Return false to cancel the action.
10940 * @param {Form} this
10941 * @param {Action} action The action to be performed
10943 beforeaction: true,
10945 * @event actionfailed
10946 * Fires when an action fails.
10947 * @param {Form} this
10948 * @param {Action} action The action that failed
10950 actionfailed : true,
10952 * @event actioncomplete
10953 * Fires when an action is completed.
10954 * @param {Form} this
10955 * @param {Action} action The action that completed
10957 actioncomplete : true
10961 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10964 * @cfg {String} method
10965 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10969 * @cfg {String} url
10970 * The URL to use for form actions if one isn't supplied in the action options.
10973 * @cfg {Boolean} fileUpload
10974 * Set to true if this form is a file upload.
10978 * @cfg {Object} baseParams
10979 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10983 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10987 * @cfg {Sting} align (left|right) for navbar forms
10992 activeAction : null,
10995 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10996 * element by passing it or its id or mask the form itself by passing in true.
10999 waitMsgTarget : false,
11004 * @cfg {Boolean} errorMask (true|false) default false
11009 * @cfg {Number} maskOffset Default 100
11014 * @cfg {Boolean} maskBody
11018 getAutoCreate : function(){
11022 method : this.method || 'POST',
11023 id : this.id || Roo.id(),
11026 if (this.parent().xtype.match(/^Nav/)) {
11027 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11031 if (this.labelAlign == 'left' ) {
11032 cfg.cls += ' form-horizontal';
11038 initEvents : function()
11040 this.el.on('submit', this.onSubmit, this);
11041 // this was added as random key presses on the form where triggering form submit.
11042 this.el.on('keypress', function(e) {
11043 if (e.getCharCode() != 13) {
11046 // we might need to allow it for textareas.. and some other items.
11047 // check e.getTarget().
11049 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11053 Roo.log("keypress blocked");
11055 e.preventDefault();
11061 onSubmit : function(e){
11066 * Returns true if client-side validation on the form is successful.
11069 isValid : function(){
11070 var items = this.getItems();
11072 var target = false;
11074 items.each(function(f){
11080 Roo.log('invalid field: ' + f.name);
11084 if(!target && f.el.isVisible(true)){
11090 if(this.errorMask && !valid){
11091 Roo.bootstrap.Form.popover.mask(this, target);
11098 * Returns true if any fields in this form have changed since their original load.
11101 isDirty : function(){
11103 var items = this.getItems();
11104 items.each(function(f){
11114 * Performs a predefined action (submit or load) or custom actions you define on this form.
11115 * @param {String} actionName The name of the action type
11116 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11117 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11118 * accept other config options):
11120 Property Type Description
11121 ---------------- --------------- ----------------------------------------------------------------------------------
11122 url String The url for the action (defaults to the form's url)
11123 method String The form method to use (defaults to the form's method, or POST if not defined)
11124 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11125 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11126 validate the form on the client (defaults to false)
11128 * @return {BasicForm} this
11130 doAction : function(action, options){
11131 if(typeof action == 'string'){
11132 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11134 if(this.fireEvent('beforeaction', this, action) !== false){
11135 this.beforeAction(action);
11136 action.run.defer(100, action);
11142 beforeAction : function(action){
11143 var o = action.options;
11148 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11150 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11153 // not really supported yet.. ??
11155 //if(this.waitMsgTarget === true){
11156 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11157 //}else if(this.waitMsgTarget){
11158 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11159 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11161 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11167 afterAction : function(action, success){
11168 this.activeAction = null;
11169 var o = action.options;
11174 Roo.get(document.body).unmask();
11180 //if(this.waitMsgTarget === true){
11181 // this.el.unmask();
11182 //}else if(this.waitMsgTarget){
11183 // this.waitMsgTarget.unmask();
11185 // Roo.MessageBox.updateProgress(1);
11186 // Roo.MessageBox.hide();
11193 Roo.callback(o.success, o.scope, [this, action]);
11194 this.fireEvent('actioncomplete', this, action);
11198 // failure condition..
11199 // we have a scenario where updates need confirming.
11200 // eg. if a locking scenario exists..
11201 // we look for { errors : { needs_confirm : true }} in the response.
11203 (typeof(action.result) != 'undefined') &&
11204 (typeof(action.result.errors) != 'undefined') &&
11205 (typeof(action.result.errors.needs_confirm) != 'undefined')
11208 Roo.log("not supported yet");
11211 Roo.MessageBox.confirm(
11212 "Change requires confirmation",
11213 action.result.errorMsg,
11218 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11228 Roo.callback(o.failure, o.scope, [this, action]);
11229 // show an error message if no failed handler is set..
11230 if (!this.hasListener('actionfailed')) {
11231 Roo.log("need to add dialog support");
11233 Roo.MessageBox.alert("Error",
11234 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11235 action.result.errorMsg :
11236 "Saving Failed, please check your entries or try again"
11241 this.fireEvent('actionfailed', this, action);
11246 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11247 * @param {String} id The value to search for
11250 findField : function(id){
11251 var items = this.getItems();
11252 var field = items.get(id);
11254 items.each(function(f){
11255 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11262 return field || null;
11265 * Mark fields in this form invalid in bulk.
11266 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11267 * @return {BasicForm} this
11269 markInvalid : function(errors){
11270 if(errors instanceof Array){
11271 for(var i = 0, len = errors.length; i < len; i++){
11272 var fieldError = errors[i];
11273 var f = this.findField(fieldError.id);
11275 f.markInvalid(fieldError.msg);
11281 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11282 field.markInvalid(errors[id]);
11286 //Roo.each(this.childForms || [], function (f) {
11287 // f.markInvalid(errors);
11294 * Set values for fields in this form in bulk.
11295 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11296 * @return {BasicForm} this
11298 setValues : function(values){
11299 if(values instanceof Array){ // array of objects
11300 for(var i = 0, len = values.length; i < len; i++){
11302 var f = this.findField(v.id);
11304 f.setValue(v.value);
11305 if(this.trackResetOnLoad){
11306 f.originalValue = f.getValue();
11310 }else{ // object hash
11313 if(typeof values[id] != 'function' && (field = this.findField(id))){
11315 if (field.setFromData &&
11316 field.valueField &&
11317 field.displayField &&
11318 // combos' with local stores can
11319 // be queried via setValue()
11320 // to set their value..
11321 (field.store && !field.store.isLocal)
11325 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11326 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11327 field.setFromData(sd);
11329 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11331 field.setFromData(values);
11334 field.setValue(values[id]);
11338 if(this.trackResetOnLoad){
11339 field.originalValue = field.getValue();
11345 //Roo.each(this.childForms || [], function (f) {
11346 // f.setValues(values);
11353 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11354 * they are returned as an array.
11355 * @param {Boolean} asString
11358 getValues : function(asString){
11359 //if (this.childForms) {
11360 // copy values from the child forms
11361 // Roo.each(this.childForms, function (f) {
11362 // this.setValues(f.getValues());
11368 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11369 if(asString === true){
11372 return Roo.urlDecode(fs);
11376 * Returns the fields in this form as an object with key/value pairs.
11377 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11380 getFieldValues : function(with_hidden)
11382 var items = this.getItems();
11384 items.each(function(f){
11386 if (!f.getName()) {
11390 var v = f.getValue();
11392 if (f.inputType =='radio') {
11393 if (typeof(ret[f.getName()]) == 'undefined') {
11394 ret[f.getName()] = ''; // empty..
11397 if (!f.el.dom.checked) {
11401 v = f.el.dom.value;
11405 if(f.xtype == 'MoneyField'){
11406 ret[f.currencyName] = f.getCurrency();
11409 // not sure if this supported any more..
11410 if ((typeof(v) == 'object') && f.getRawValue) {
11411 v = f.getRawValue() ; // dates..
11413 // combo boxes where name != hiddenName...
11414 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11415 ret[f.name] = f.getRawValue();
11417 ret[f.getName()] = v;
11424 * Clears all invalid messages in this form.
11425 * @return {BasicForm} this
11427 clearInvalid : function(){
11428 var items = this.getItems();
11430 items.each(function(f){
11438 * Resets this form.
11439 * @return {BasicForm} this
11441 reset : function(){
11442 var items = this.getItems();
11443 items.each(function(f){
11447 Roo.each(this.childForms || [], function (f) {
11455 getItems : function()
11457 var r=new Roo.util.MixedCollection(false, function(o){
11458 return o.id || (o.id = Roo.id());
11460 var iter = function(el) {
11467 Roo.each(el.items,function(e) {
11476 hideFields : function(items)
11478 Roo.each(items, function(i){
11480 var f = this.findField(i);
11491 showFields : function(items)
11493 Roo.each(items, function(i){
11495 var f = this.findField(i);
11508 Roo.apply(Roo.bootstrap.Form, {
11524 intervalID : false,
11530 if(this.isApplied){
11535 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11536 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11537 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11538 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11541 this.maskEl.top.enableDisplayMode("block");
11542 this.maskEl.left.enableDisplayMode("block");
11543 this.maskEl.bottom.enableDisplayMode("block");
11544 this.maskEl.right.enableDisplayMode("block");
11546 this.toolTip = new Roo.bootstrap.Tooltip({
11547 cls : 'roo-form-error-popover',
11549 'left' : ['r-l', [-2,0], 'right'],
11550 'right' : ['l-r', [2,0], 'left'],
11551 'bottom' : ['tl-bl', [0,2], 'top'],
11552 'top' : [ 'bl-tl', [0,-2], 'bottom']
11556 this.toolTip.render(Roo.get(document.body));
11558 this.toolTip.el.enableDisplayMode("block");
11560 Roo.get(document.body).on('click', function(){
11564 Roo.get(document.body).on('touchstart', function(){
11568 this.isApplied = true
11571 mask : function(form, target)
11575 this.target = target;
11577 if(!this.form.errorMask || !target.el){
11581 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11583 Roo.log(scrollable);
11585 var ot = this.target.el.calcOffsetsTo(scrollable);
11587 var scrollTo = ot[1] - this.form.maskOffset;
11589 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11591 scrollable.scrollTo('top', scrollTo);
11593 var box = this.target.el.getBox();
11595 var zIndex = Roo.bootstrap.Modal.zIndex++;
11598 this.maskEl.top.setStyle('position', 'absolute');
11599 this.maskEl.top.setStyle('z-index', zIndex);
11600 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11601 this.maskEl.top.setLeft(0);
11602 this.maskEl.top.setTop(0);
11603 this.maskEl.top.show();
11605 this.maskEl.left.setStyle('position', 'absolute');
11606 this.maskEl.left.setStyle('z-index', zIndex);
11607 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11608 this.maskEl.left.setLeft(0);
11609 this.maskEl.left.setTop(box.y - this.padding);
11610 this.maskEl.left.show();
11612 this.maskEl.bottom.setStyle('position', 'absolute');
11613 this.maskEl.bottom.setStyle('z-index', zIndex);
11614 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11615 this.maskEl.bottom.setLeft(0);
11616 this.maskEl.bottom.setTop(box.bottom + this.padding);
11617 this.maskEl.bottom.show();
11619 this.maskEl.right.setStyle('position', 'absolute');
11620 this.maskEl.right.setStyle('z-index', zIndex);
11621 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11622 this.maskEl.right.setLeft(box.right + this.padding);
11623 this.maskEl.right.setTop(box.y - this.padding);
11624 this.maskEl.right.show();
11626 this.toolTip.bindEl = this.target.el;
11628 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11630 var tip = this.target.blankText;
11632 if(this.target.getValue() !== '' ) {
11634 if (this.target.invalidText.length) {
11635 tip = this.target.invalidText;
11636 } else if (this.target.regexText.length){
11637 tip = this.target.regexText;
11641 this.toolTip.show(tip);
11643 this.intervalID = window.setInterval(function() {
11644 Roo.bootstrap.Form.popover.unmask();
11647 window.onwheel = function(){ return false;};
11649 (function(){ this.isMasked = true; }).defer(500, this);
11653 unmask : function()
11655 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11659 this.maskEl.top.setStyle('position', 'absolute');
11660 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11661 this.maskEl.top.hide();
11663 this.maskEl.left.setStyle('position', 'absolute');
11664 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11665 this.maskEl.left.hide();
11667 this.maskEl.bottom.setStyle('position', 'absolute');
11668 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11669 this.maskEl.bottom.hide();
11671 this.maskEl.right.setStyle('position', 'absolute');
11672 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11673 this.maskEl.right.hide();
11675 this.toolTip.hide();
11677 this.toolTip.el.hide();
11679 window.onwheel = function(){ return true;};
11681 if(this.intervalID){
11682 window.clearInterval(this.intervalID);
11683 this.intervalID = false;
11686 this.isMasked = false;
11696 * Ext JS Library 1.1.1
11697 * Copyright(c) 2006-2007, Ext JS, LLC.
11699 * Originally Released Under LGPL - original licence link has changed is not relivant.
11702 * <script type="text/javascript">
11705 * @class Roo.form.VTypes
11706 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11709 Roo.form.VTypes = function(){
11710 // closure these in so they are only created once.
11711 var alpha = /^[a-zA-Z_]+$/;
11712 var alphanum = /^[a-zA-Z0-9_]+$/;
11713 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11714 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11716 // All these messages and functions are configurable
11719 * The function used to validate email addresses
11720 * @param {String} value The email address
11722 'email' : function(v){
11723 return email.test(v);
11726 * The error text to display when the email validation function returns false
11729 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11731 * The keystroke filter mask to be applied on email input
11734 'emailMask' : /[a-z0-9_\.\-@]/i,
11737 * The function used to validate URLs
11738 * @param {String} value The URL
11740 'url' : function(v){
11741 return url.test(v);
11744 * The error text to display when the url validation function returns false
11747 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11750 * The function used to validate alpha values
11751 * @param {String} value The value
11753 'alpha' : function(v){
11754 return alpha.test(v);
11757 * The error text to display when the alpha validation function returns false
11760 'alphaText' : 'This field should only contain letters and _',
11762 * The keystroke filter mask to be applied on alpha input
11765 'alphaMask' : /[a-z_]/i,
11768 * The function used to validate alphanumeric values
11769 * @param {String} value The value
11771 'alphanum' : function(v){
11772 return alphanum.test(v);
11775 * The error text to display when the alphanumeric validation function returns false
11778 'alphanumText' : 'This field should only contain letters, numbers and _',
11780 * The keystroke filter mask to be applied on alphanumeric input
11783 'alphanumMask' : /[a-z0-9_]/i
11793 * @class Roo.bootstrap.Input
11794 * @extends Roo.bootstrap.Component
11795 * Bootstrap Input class
11796 * @cfg {Boolean} disabled is it disabled
11797 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11798 * @cfg {String} name name of the input
11799 * @cfg {string} fieldLabel - the label associated
11800 * @cfg {string} placeholder - placeholder to put in text.
11801 * @cfg {string} before - input group add on before
11802 * @cfg {string} after - input group add on after
11803 * @cfg {string} size - (lg|sm) or leave empty..
11804 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11805 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11806 * @cfg {Number} md colspan out of 12 for computer-sized screens
11807 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11808 * @cfg {string} value default value of the input
11809 * @cfg {Number} labelWidth set the width of label
11810 * @cfg {Number} labellg set the width of label (1-12)
11811 * @cfg {Number} labelmd set the width of label (1-12)
11812 * @cfg {Number} labelsm set the width of label (1-12)
11813 * @cfg {Number} labelxs set the width of label (1-12)
11814 * @cfg {String} labelAlign (top|left)
11815 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11816 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11817 * @cfg {String} indicatorpos (left|right) default left
11818 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11819 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11820 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11822 * @cfg {String} align (left|center|right) Default left
11823 * @cfg {Boolean} forceFeedback (true|false) Default false
11826 * Create a new Input
11827 * @param {Object} config The config object
11830 Roo.bootstrap.Input = function(config){
11832 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11837 * Fires when this field receives input focus.
11838 * @param {Roo.form.Field} this
11843 * Fires when this field loses input focus.
11844 * @param {Roo.form.Field} this
11848 * @event specialkey
11849 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11850 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11851 * @param {Roo.form.Field} this
11852 * @param {Roo.EventObject} e The event object
11857 * Fires just before the field blurs if the field value has changed.
11858 * @param {Roo.form.Field} this
11859 * @param {Mixed} newValue The new value
11860 * @param {Mixed} oldValue The original value
11865 * Fires after the field has been marked as invalid.
11866 * @param {Roo.form.Field} this
11867 * @param {String} msg The validation message
11872 * Fires after the field has been validated with no errors.
11873 * @param {Roo.form.Field} this
11878 * Fires after the key up
11879 * @param {Roo.form.Field} this
11880 * @param {Roo.EventObject} e The event Object
11885 * Fires after the user pastes into input
11886 * @param {Roo.form.Field} this
11887 * @param {Roo.EventObject} e The event Object
11893 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11895 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11896 automatic validation (defaults to "keyup").
11898 validationEvent : "keyup",
11900 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11902 validateOnBlur : true,
11904 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11906 validationDelay : 250,
11908 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11910 focusClass : "x-form-focus", // not needed???
11914 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11916 invalidClass : "has-warning",
11919 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11921 validClass : "has-success",
11924 * @cfg {Boolean} hasFeedback (true|false) default true
11926 hasFeedback : true,
11929 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11931 invalidFeedbackClass : "glyphicon-warning-sign",
11934 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11936 validFeedbackClass : "glyphicon-ok",
11939 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11941 selectOnFocus : false,
11944 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11948 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11953 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11955 disableKeyFilter : false,
11958 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11962 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11966 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11968 blankText : "Please complete this mandatory field",
11971 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11975 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11977 maxLength : Number.MAX_VALUE,
11979 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11981 minLengthText : "The minimum length for this field is {0}",
11983 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11985 maxLengthText : "The maximum length for this field is {0}",
11989 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11990 * If available, this function will be called only after the basic validators all return true, and will be passed the
11991 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11995 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11996 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11997 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12001 * @cfg {String} regexText -- Depricated - use Invalid Text
12006 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12012 autocomplete: false,
12016 inputType : 'text',
12019 placeholder: false,
12024 preventMark: false,
12025 isFormField : true,
12028 labelAlign : false,
12031 formatedValue : false,
12032 forceFeedback : false,
12034 indicatorpos : 'left',
12044 parentLabelAlign : function()
12047 while (parent.parent()) {
12048 parent = parent.parent();
12049 if (typeof(parent.labelAlign) !='undefined') {
12050 return parent.labelAlign;
12057 getAutoCreate : function()
12059 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12065 if(this.inputType != 'hidden'){
12066 cfg.cls = 'form-group' //input-group
12072 type : this.inputType,
12073 value : this.value,
12074 cls : 'form-control',
12075 placeholder : this.placeholder || '',
12076 autocomplete : this.autocomplete || 'new-password'
12078 if (this.inputType == 'file') {
12079 input.style = 'overflow:hidden'; // why not in CSS?
12082 if(this.capture.length){
12083 input.capture = this.capture;
12086 if(this.accept.length){
12087 input.accept = this.accept + "/*";
12091 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12094 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12095 input.maxLength = this.maxLength;
12098 if (this.disabled) {
12099 input.disabled=true;
12102 if (this.readOnly) {
12103 input.readonly=true;
12107 input.name = this.name;
12111 input.cls += ' input-' + this.size;
12115 ['xs','sm','md','lg'].map(function(size){
12116 if (settings[size]) {
12117 cfg.cls += ' col-' + size + '-' + settings[size];
12121 var inputblock = input;
12125 cls: 'glyphicon form-control-feedback'
12128 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12131 cls : 'has-feedback',
12139 if (this.before || this.after) {
12142 cls : 'input-group',
12146 if (this.before && typeof(this.before) == 'string') {
12148 inputblock.cn.push({
12150 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12154 if (this.before && typeof(this.before) == 'object') {
12155 this.before = Roo.factory(this.before);
12157 inputblock.cn.push({
12159 cls : 'roo-input-before input-group-prepend input-group-' +
12160 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12164 inputblock.cn.push(input);
12166 if (this.after && typeof(this.after) == 'string') {
12167 inputblock.cn.push({
12169 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12173 if (this.after && typeof(this.after) == 'object') {
12174 this.after = Roo.factory(this.after);
12176 inputblock.cn.push({
12178 cls : 'roo-input-after input-group-append input-group-' +
12179 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12183 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12184 inputblock.cls += ' has-feedback';
12185 inputblock.cn.push(feedback);
12190 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12191 tooltip : 'This field is required'
12193 if (this.allowBlank ) {
12194 indicator.style = this.allowBlank ? ' display:none' : '';
12196 if (align ==='left' && this.fieldLabel.length) {
12198 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12205 cls : 'control-label col-form-label',
12206 html : this.fieldLabel
12217 var labelCfg = cfg.cn[1];
12218 var contentCfg = cfg.cn[2];
12220 if(this.indicatorpos == 'right'){
12225 cls : 'control-label col-form-label',
12229 html : this.fieldLabel
12243 labelCfg = cfg.cn[0];
12244 contentCfg = cfg.cn[1];
12248 if(this.labelWidth > 12){
12249 labelCfg.style = "width: " + this.labelWidth + 'px';
12252 if(this.labelWidth < 13 && this.labelmd == 0){
12253 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12256 if(this.labellg > 0){
12257 labelCfg.cls += ' col-lg-' + this.labellg;
12258 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12261 if(this.labelmd > 0){
12262 labelCfg.cls += ' col-md-' + this.labelmd;
12263 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12266 if(this.labelsm > 0){
12267 labelCfg.cls += ' col-sm-' + this.labelsm;
12268 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12271 if(this.labelxs > 0){
12272 labelCfg.cls += ' col-xs-' + this.labelxs;
12273 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12277 } else if ( this.fieldLabel.length) {
12284 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12285 tooltip : 'This field is required',
12286 style : this.allowBlank ? ' display:none' : ''
12290 //cls : 'input-group-addon',
12291 html : this.fieldLabel
12299 if(this.indicatorpos == 'right'){
12304 //cls : 'input-group-addon',
12305 html : this.fieldLabel
12310 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12311 tooltip : 'This field is required',
12312 style : this.allowBlank ? ' display:none' : ''
12332 if (this.parentType === 'Navbar' && this.parent().bar) {
12333 cfg.cls += ' navbar-form';
12336 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12337 // on BS4 we do this only if not form
12338 cfg.cls += ' navbar-form';
12346 * return the real input element.
12348 inputEl: function ()
12350 return this.el.select('input.form-control',true).first();
12353 tooltipEl : function()
12355 return this.inputEl();
12358 indicatorEl : function()
12360 if (Roo.bootstrap.version == 4) {
12361 return false; // not enabled in v4 yet.
12364 var indicator = this.el.select('i.roo-required-indicator',true).first();
12374 setDisabled : function(v)
12376 var i = this.inputEl().dom;
12378 i.removeAttribute('disabled');
12382 i.setAttribute('disabled','true');
12384 initEvents : function()
12387 this.inputEl().on("keydown" , this.fireKey, this);
12388 this.inputEl().on("focus", this.onFocus, this);
12389 this.inputEl().on("blur", this.onBlur, this);
12391 this.inputEl().relayEvent('keyup', this);
12392 this.inputEl().relayEvent('paste', this);
12394 this.indicator = this.indicatorEl();
12396 if(this.indicator){
12397 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12400 // reference to original value for reset
12401 this.originalValue = this.getValue();
12402 //Roo.form.TextField.superclass.initEvents.call(this);
12403 if(this.validationEvent == 'keyup'){
12404 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12405 this.inputEl().on('keyup', this.filterValidation, this);
12407 else if(this.validationEvent !== false){
12408 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12411 if(this.selectOnFocus){
12412 this.on("focus", this.preFocus, this);
12415 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12416 this.inputEl().on("keypress", this.filterKeys, this);
12418 this.inputEl().relayEvent('keypress', this);
12421 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12422 this.el.on("click", this.autoSize, this);
12425 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12426 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12429 if (typeof(this.before) == 'object') {
12430 this.before.render(this.el.select('.roo-input-before',true).first());
12432 if (typeof(this.after) == 'object') {
12433 this.after.render(this.el.select('.roo-input-after',true).first());
12436 this.inputEl().on('change', this.onChange, this);
12439 filterValidation : function(e){
12440 if(!e.isNavKeyPress()){
12441 this.validationTask.delay(this.validationDelay);
12445 * Validates the field value
12446 * @return {Boolean} True if the value is valid, else false
12448 validate : function(){
12449 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12450 if(this.disabled || this.validateValue(this.getRawValue())){
12455 this.markInvalid();
12461 * Validates a value according to the field's validation rules and marks the field as invalid
12462 * if the validation fails
12463 * @param {Mixed} value The value to validate
12464 * @return {Boolean} True if the value is valid, else false
12466 validateValue : function(value)
12468 if(this.getVisibilityEl().hasClass('hidden')){
12472 if(value.length < 1) { // if it's blank
12473 if(this.allowBlank){
12479 if(value.length < this.minLength){
12482 if(value.length > this.maxLength){
12486 var vt = Roo.form.VTypes;
12487 if(!vt[this.vtype](value, this)){
12491 if(typeof this.validator == "function"){
12492 var msg = this.validator(value);
12496 if (typeof(msg) == 'string') {
12497 this.invalidText = msg;
12501 if(this.regex && !this.regex.test(value)){
12509 fireKey : function(e){
12510 //Roo.log('field ' + e.getKey());
12511 if(e.isNavKeyPress()){
12512 this.fireEvent("specialkey", this, e);
12515 focus : function (selectText){
12517 this.inputEl().focus();
12518 if(selectText === true){
12519 this.inputEl().dom.select();
12525 onFocus : function(){
12526 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12527 // this.el.addClass(this.focusClass);
12529 if(!this.hasFocus){
12530 this.hasFocus = true;
12531 this.startValue = this.getValue();
12532 this.fireEvent("focus", this);
12536 beforeBlur : Roo.emptyFn,
12540 onBlur : function(){
12542 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12543 //this.el.removeClass(this.focusClass);
12545 this.hasFocus = false;
12546 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12549 var v = this.getValue();
12550 if(String(v) !== String(this.startValue)){
12551 this.fireEvent('change', this, v, this.startValue);
12553 this.fireEvent("blur", this);
12556 onChange : function(e)
12558 var v = this.getValue();
12559 if(String(v) !== String(this.startValue)){
12560 this.fireEvent('change', this, v, this.startValue);
12566 * Resets the current field value to the originally loaded value and clears any validation messages
12568 reset : function(){
12569 this.setValue(this.originalValue);
12573 * Returns the name of the field
12574 * @return {Mixed} name The name field
12576 getName: function(){
12580 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12581 * @return {Mixed} value The field value
12583 getValue : function(){
12585 var v = this.inputEl().getValue();
12590 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12591 * @return {Mixed} value The field value
12593 getRawValue : function(){
12594 var v = this.inputEl().getValue();
12600 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12601 * @param {Mixed} value The value to set
12603 setRawValue : function(v){
12604 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12607 selectText : function(start, end){
12608 var v = this.getRawValue();
12610 start = start === undefined ? 0 : start;
12611 end = end === undefined ? v.length : end;
12612 var d = this.inputEl().dom;
12613 if(d.setSelectionRange){
12614 d.setSelectionRange(start, end);
12615 }else if(d.createTextRange){
12616 var range = d.createTextRange();
12617 range.moveStart("character", start);
12618 range.moveEnd("character", v.length-end);
12625 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12626 * @param {Mixed} value The value to set
12628 setValue : function(v){
12631 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12637 processValue : function(value){
12638 if(this.stripCharsRe){
12639 var newValue = value.replace(this.stripCharsRe, '');
12640 if(newValue !== value){
12641 this.setRawValue(newValue);
12648 preFocus : function(){
12650 if(this.selectOnFocus){
12651 this.inputEl().dom.select();
12654 filterKeys : function(e){
12655 var k = e.getKey();
12656 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12659 var c = e.getCharCode(), cc = String.fromCharCode(c);
12660 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12663 if(!this.maskRe.test(cc)){
12668 * Clear any invalid styles/messages for this field
12670 clearInvalid : function(){
12672 if(!this.el || this.preventMark){ // not rendered
12677 this.el.removeClass([this.invalidClass, 'is-invalid']);
12679 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12681 var feedback = this.el.select('.form-control-feedback', true).first();
12684 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12689 if(this.indicator){
12690 this.indicator.removeClass('visible');
12691 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12694 this.fireEvent('valid', this);
12698 * Mark this field as valid
12700 markValid : function()
12702 if(!this.el || this.preventMark){ // not rendered...
12706 this.el.removeClass([this.invalidClass, this.validClass]);
12707 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12709 var feedback = this.el.select('.form-control-feedback', true).first();
12712 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12715 if(this.indicator){
12716 this.indicator.removeClass('visible');
12717 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12725 if(this.allowBlank && !this.getRawValue().length){
12728 if (Roo.bootstrap.version == 3) {
12729 this.el.addClass(this.validClass);
12731 this.inputEl().addClass('is-valid');
12734 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12736 var feedback = this.el.select('.form-control-feedback', true).first();
12739 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12740 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12745 this.fireEvent('valid', this);
12749 * Mark this field as invalid
12750 * @param {String} msg The validation message
12752 markInvalid : function(msg)
12754 if(!this.el || this.preventMark){ // not rendered
12758 this.el.removeClass([this.invalidClass, this.validClass]);
12759 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12761 var feedback = this.el.select('.form-control-feedback', true).first();
12764 this.el.select('.form-control-feedback', true).first().removeClass(
12765 [this.invalidFeedbackClass, this.validFeedbackClass]);
12772 if(this.allowBlank && !this.getRawValue().length){
12776 if(this.indicator){
12777 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12778 this.indicator.addClass('visible');
12780 if (Roo.bootstrap.version == 3) {
12781 this.el.addClass(this.invalidClass);
12783 this.inputEl().addClass('is-invalid');
12788 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12790 var feedback = this.el.select('.form-control-feedback', true).first();
12793 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12795 if(this.getValue().length || this.forceFeedback){
12796 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12803 this.fireEvent('invalid', this, msg);
12806 SafariOnKeyDown : function(event)
12808 // this is a workaround for a password hang bug on chrome/ webkit.
12809 if (this.inputEl().dom.type != 'password') {
12813 var isSelectAll = false;
12815 if(this.inputEl().dom.selectionEnd > 0){
12816 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12818 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12819 event.preventDefault();
12824 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12826 event.preventDefault();
12827 // this is very hacky as keydown always get's upper case.
12829 var cc = String.fromCharCode(event.getCharCode());
12830 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12834 adjustWidth : function(tag, w){
12835 tag = tag.toLowerCase();
12836 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12837 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12838 if(tag == 'input'){
12841 if(tag == 'textarea'){
12844 }else if(Roo.isOpera){
12845 if(tag == 'input'){
12848 if(tag == 'textarea'){
12856 setFieldLabel : function(v)
12858 if(!this.rendered){
12862 if(this.indicatorEl()){
12863 var ar = this.el.select('label > span',true);
12865 if (ar.elements.length) {
12866 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12867 this.fieldLabel = v;
12871 var br = this.el.select('label',true);
12873 if(br.elements.length) {
12874 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12875 this.fieldLabel = v;
12879 Roo.log('Cannot Found any of label > span || label in input');
12883 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12884 this.fieldLabel = v;
12899 * @class Roo.bootstrap.TextArea
12900 * @extends Roo.bootstrap.Input
12901 * Bootstrap TextArea class
12902 * @cfg {Number} cols Specifies the visible width of a text area
12903 * @cfg {Number} rows Specifies the visible number of lines in a text area
12904 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12905 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12906 * @cfg {string} html text
12909 * Create a new TextArea
12910 * @param {Object} config The config object
12913 Roo.bootstrap.TextArea = function(config){
12914 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12918 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12928 getAutoCreate : function(){
12930 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12936 if(this.inputType != 'hidden'){
12937 cfg.cls = 'form-group' //input-group
12945 value : this.value || '',
12946 html: this.html || '',
12947 cls : 'form-control',
12948 placeholder : this.placeholder || ''
12952 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12953 input.maxLength = this.maxLength;
12957 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12961 input.cols = this.cols;
12964 if (this.readOnly) {
12965 input.readonly = true;
12969 input.name = this.name;
12973 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12977 ['xs','sm','md','lg'].map(function(size){
12978 if (settings[size]) {
12979 cfg.cls += ' col-' + size + '-' + settings[size];
12983 var inputblock = input;
12985 if(this.hasFeedback && !this.allowBlank){
12989 cls: 'glyphicon form-control-feedback'
12993 cls : 'has-feedback',
13002 if (this.before || this.after) {
13005 cls : 'input-group',
13009 inputblock.cn.push({
13011 cls : 'input-group-addon',
13016 inputblock.cn.push(input);
13018 if(this.hasFeedback && !this.allowBlank){
13019 inputblock.cls += ' has-feedback';
13020 inputblock.cn.push(feedback);
13024 inputblock.cn.push({
13026 cls : 'input-group-addon',
13033 if (align ==='left' && this.fieldLabel.length) {
13038 cls : 'control-label',
13039 html : this.fieldLabel
13050 if(this.labelWidth > 12){
13051 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13054 if(this.labelWidth < 13 && this.labelmd == 0){
13055 this.labelmd = this.labelWidth;
13058 if(this.labellg > 0){
13059 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13060 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13063 if(this.labelmd > 0){
13064 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13065 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13068 if(this.labelsm > 0){
13069 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13070 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13073 if(this.labelxs > 0){
13074 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13075 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13078 } else if ( this.fieldLabel.length) {
13083 //cls : 'input-group-addon',
13084 html : this.fieldLabel
13102 if (this.disabled) {
13103 input.disabled=true;
13110 * return the real textarea element.
13112 inputEl: function ()
13114 return this.el.select('textarea.form-control',true).first();
13118 * Clear any invalid styles/messages for this field
13120 clearInvalid : function()
13123 if(!this.el || this.preventMark){ // not rendered
13127 var label = this.el.select('label', true).first();
13128 var icon = this.el.select('i.fa-star', true).first();
13133 this.el.removeClass( this.validClass);
13134 this.inputEl().removeClass('is-invalid');
13136 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13138 var feedback = this.el.select('.form-control-feedback', true).first();
13141 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13146 this.fireEvent('valid', this);
13150 * Mark this field as valid
13152 markValid : function()
13154 if(!this.el || this.preventMark){ // not rendered
13158 this.el.removeClass([this.invalidClass, this.validClass]);
13159 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13161 var feedback = this.el.select('.form-control-feedback', true).first();
13164 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13167 if(this.disabled || this.allowBlank){
13171 var label = this.el.select('label', true).first();
13172 var icon = this.el.select('i.fa-star', true).first();
13177 if (Roo.bootstrap.version == 3) {
13178 this.el.addClass(this.validClass);
13180 this.inputEl().addClass('is-valid');
13184 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13186 var feedback = this.el.select('.form-control-feedback', true).first();
13189 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13190 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13195 this.fireEvent('valid', this);
13199 * Mark this field as invalid
13200 * @param {String} msg The validation message
13202 markInvalid : function(msg)
13204 if(!this.el || this.preventMark){ // not rendered
13208 this.el.removeClass([this.invalidClass, this.validClass]);
13209 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13211 var feedback = this.el.select('.form-control-feedback', true).first();
13214 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13217 if(this.disabled || this.allowBlank){
13221 var label = this.el.select('label', true).first();
13222 var icon = this.el.select('i.fa-star', true).first();
13224 if(!this.getValue().length && label && !icon){
13225 this.el.createChild({
13227 cls : 'text-danger fa fa-lg fa-star',
13228 tooltip : 'This field is required',
13229 style : 'margin-right:5px;'
13233 if (Roo.bootstrap.version == 3) {
13234 this.el.addClass(this.invalidClass);
13236 this.inputEl().addClass('is-invalid');
13239 // fixme ... this may be depricated need to test..
13240 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13242 var feedback = this.el.select('.form-control-feedback', true).first();
13245 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13247 if(this.getValue().length || this.forceFeedback){
13248 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13255 this.fireEvent('invalid', this, msg);
13263 * trigger field - base class for combo..
13268 * @class Roo.bootstrap.TriggerField
13269 * @extends Roo.bootstrap.Input
13270 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13271 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13272 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13273 * for which you can provide a custom implementation. For example:
13275 var trigger = new Roo.bootstrap.TriggerField();
13276 trigger.onTriggerClick = myTriggerFn;
13277 trigger.applyTo('my-field');
13280 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13281 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13282 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13283 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13284 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13287 * Create a new TriggerField.
13288 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13289 * to the base TextField)
13291 Roo.bootstrap.TriggerField = function(config){
13292 this.mimicing = false;
13293 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13296 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13298 * @cfg {String} triggerClass A CSS class to apply to the trigger
13301 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13306 * @cfg {Boolean} removable (true|false) special filter default false
13310 /** @cfg {Boolean} grow @hide */
13311 /** @cfg {Number} growMin @hide */
13312 /** @cfg {Number} growMax @hide */
13318 autoSize: Roo.emptyFn,
13322 deferHeight : true,
13325 actionMode : 'wrap',
13330 getAutoCreate : function(){
13332 var align = this.labelAlign || this.parentLabelAlign();
13337 cls: 'form-group' //input-group
13344 type : this.inputType,
13345 cls : 'form-control',
13346 autocomplete: 'new-password',
13347 placeholder : this.placeholder || ''
13351 input.name = this.name;
13354 input.cls += ' input-' + this.size;
13357 if (this.disabled) {
13358 input.disabled=true;
13361 var inputblock = input;
13363 if(this.hasFeedback && !this.allowBlank){
13367 cls: 'glyphicon form-control-feedback'
13370 if(this.removable && !this.editable ){
13372 cls : 'has-feedback',
13378 cls : 'roo-combo-removable-btn close'
13385 cls : 'has-feedback',
13394 if(this.removable && !this.editable ){
13396 cls : 'roo-removable',
13402 cls : 'roo-combo-removable-btn close'
13409 if (this.before || this.after) {
13412 cls : 'input-group',
13416 inputblock.cn.push({
13418 cls : 'input-group-addon input-group-prepend input-group-text',
13423 inputblock.cn.push(input);
13425 if(this.hasFeedback && !this.allowBlank){
13426 inputblock.cls += ' has-feedback';
13427 inputblock.cn.push(feedback);
13431 inputblock.cn.push({
13433 cls : 'input-group-addon input-group-append input-group-text',
13442 var ibwrap = inputblock;
13447 cls: 'roo-select2-choices',
13451 cls: 'roo-select2-search-field',
13463 cls: 'roo-select2-container input-group',
13468 cls: 'form-hidden-field'
13474 if(!this.multiple && this.showToggleBtn){
13480 if (this.caret != false) {
13483 cls: 'fa fa-' + this.caret
13490 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13492 Roo.bootstrap.version == 3 ? caret : '',
13495 cls: 'combobox-clear',
13509 combobox.cls += ' roo-select2-container-multi';
13513 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13514 tooltip : 'This field is required'
13516 if (Roo.bootstrap.version == 4) {
13519 style : 'display:none'
13524 if (align ==='left' && this.fieldLabel.length) {
13526 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13533 cls : 'control-label',
13534 html : this.fieldLabel
13546 var labelCfg = cfg.cn[1];
13547 var contentCfg = cfg.cn[2];
13549 if(this.indicatorpos == 'right'){
13554 cls : 'control-label',
13558 html : this.fieldLabel
13572 labelCfg = cfg.cn[0];
13573 contentCfg = cfg.cn[1];
13576 if(this.labelWidth > 12){
13577 labelCfg.style = "width: " + this.labelWidth + 'px';
13580 if(this.labelWidth < 13 && this.labelmd == 0){
13581 this.labelmd = this.labelWidth;
13584 if(this.labellg > 0){
13585 labelCfg.cls += ' col-lg-' + this.labellg;
13586 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13589 if(this.labelmd > 0){
13590 labelCfg.cls += ' col-md-' + this.labelmd;
13591 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13594 if(this.labelsm > 0){
13595 labelCfg.cls += ' col-sm-' + this.labelsm;
13596 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13599 if(this.labelxs > 0){
13600 labelCfg.cls += ' col-xs-' + this.labelxs;
13601 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13604 } else if ( this.fieldLabel.length) {
13605 // Roo.log(" label");
13610 //cls : 'input-group-addon',
13611 html : this.fieldLabel
13619 if(this.indicatorpos == 'right'){
13627 html : this.fieldLabel
13641 // Roo.log(" no label && no align");
13648 ['xs','sm','md','lg'].map(function(size){
13649 if (settings[size]) {
13650 cfg.cls += ' col-' + size + '-' + settings[size];
13661 onResize : function(w, h){
13662 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13663 // if(typeof w == 'number'){
13664 // var x = w - this.trigger.getWidth();
13665 // this.inputEl().setWidth(this.adjustWidth('input', x));
13666 // this.trigger.setStyle('left', x+'px');
13671 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13674 getResizeEl : function(){
13675 return this.inputEl();
13679 getPositionEl : function(){
13680 return this.inputEl();
13684 alignErrorIcon : function(){
13685 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13689 initEvents : function(){
13693 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13694 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13695 if(!this.multiple && this.showToggleBtn){
13696 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13697 if(this.hideTrigger){
13698 this.trigger.setDisplayed(false);
13700 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13704 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13707 if(this.removable && !this.editable && !this.tickable){
13708 var close = this.closeTriggerEl();
13711 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13712 close.on('click', this.removeBtnClick, this, close);
13716 //this.trigger.addClassOnOver('x-form-trigger-over');
13717 //this.trigger.addClassOnClick('x-form-trigger-click');
13720 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13724 closeTriggerEl : function()
13726 var close = this.el.select('.roo-combo-removable-btn', true).first();
13727 return close ? close : false;
13730 removeBtnClick : function(e, h, el)
13732 e.preventDefault();
13734 if(this.fireEvent("remove", this) !== false){
13736 this.fireEvent("afterremove", this)
13740 createList : function()
13742 this.list = Roo.get(document.body).createChild({
13743 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13744 cls: 'typeahead typeahead-long dropdown-menu shadow',
13745 style: 'display:none'
13748 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13753 initTrigger : function(){
13758 onDestroy : function(){
13760 this.trigger.removeAllListeners();
13761 // this.trigger.remove();
13764 // this.wrap.remove();
13766 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13770 onFocus : function(){
13771 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13773 if(!this.mimicing){
13774 this.wrap.addClass('x-trigger-wrap-focus');
13775 this.mimicing = true;
13776 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13777 if(this.monitorTab){
13778 this.el.on("keydown", this.checkTab, this);
13785 checkTab : function(e){
13786 if(e.getKey() == e.TAB){
13787 this.triggerBlur();
13792 onBlur : function(){
13797 mimicBlur : function(e, t){
13799 if(!this.wrap.contains(t) && this.validateBlur()){
13800 this.triggerBlur();
13806 triggerBlur : function(){
13807 this.mimicing = false;
13808 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13809 if(this.monitorTab){
13810 this.el.un("keydown", this.checkTab, this);
13812 //this.wrap.removeClass('x-trigger-wrap-focus');
13813 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13817 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13818 validateBlur : function(e, t){
13823 onDisable : function(){
13824 this.inputEl().dom.disabled = true;
13825 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13827 // this.wrap.addClass('x-item-disabled');
13832 onEnable : function(){
13833 this.inputEl().dom.disabled = false;
13834 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13836 // this.el.removeClass('x-item-disabled');
13841 onShow : function(){
13842 var ae = this.getActionEl();
13845 ae.dom.style.display = '';
13846 ae.dom.style.visibility = 'visible';
13852 onHide : function(){
13853 var ae = this.getActionEl();
13854 ae.dom.style.display = 'none';
13858 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13859 * by an implementing function.
13861 * @param {EventObject} e
13863 onTriggerClick : Roo.emptyFn
13871 * @class Roo.bootstrap.CardUploader
13872 * @extends Roo.bootstrap.Button
13873 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13874 * @cfg {Number} errorTimeout default 3000
13875 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13876 * @cfg {Array} html The button text.
13880 * Create a new CardUploader
13881 * @param {Object} config The config object
13884 Roo.bootstrap.CardUploader = function(config){
13888 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13891 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13899 * When a image is clicked on - and needs to display a slideshow or similar..
13900 * @param {Roo.bootstrap.Card} this
13901 * @param {Object} The image information data
13907 * When a the download link is clicked
13908 * @param {Roo.bootstrap.Card} this
13909 * @param {Object} The image information data contains
13916 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13919 errorTimeout : 3000,
13923 fileCollection : false,
13926 getAutoCreate : function()
13930 cls :'form-group' ,
13935 //cls : 'input-group-addon',
13936 html : this.fieldLabel
13944 value : this.value,
13945 cls : 'd-none form-control'
13950 multiple : 'multiple',
13952 cls : 'd-none roo-card-upload-selector'
13956 cls : 'roo-card-uploader-button-container w-100 mb-2'
13959 cls : 'card-columns roo-card-uploader-container'
13969 getChildContainer : function() /// what children are added to.
13971 return this.containerEl;
13974 getButtonContainer : function() /// what children are added to.
13976 return this.el.select(".roo-card-uploader-button-container").first();
13979 initEvents : function()
13982 Roo.bootstrap.Input.prototype.initEvents.call(this);
13986 xns: Roo.bootstrap,
13989 container_method : 'getButtonContainer' ,
13990 html : this.html, // fix changable?
13993 'click' : function(btn, e) {
14002 this.urlAPI = (window.createObjectURL && window) ||
14003 (window.URL && URL.revokeObjectURL && URL) ||
14004 (window.webkitURL && webkitURL);
14009 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14011 this.selectorEl.on('change', this.onFileSelected, this);
14014 this.images.forEach(function(img) {
14017 this.images = false;
14019 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14025 onClick : function(e)
14027 e.preventDefault();
14029 this.selectorEl.dom.click();
14033 onFileSelected : function(e)
14035 e.preventDefault();
14037 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14041 Roo.each(this.selectorEl.dom.files, function(file){
14042 this.addFile(file);
14051 addFile : function(file)
14054 if(typeof(file) === 'string'){
14055 throw "Add file by name?"; // should not happen
14059 if(!file || !this.urlAPI){
14069 var url = _this.urlAPI.createObjectURL( file);
14072 id : Roo.bootstrap.CardUploader.ID--,
14073 is_uploaded : false,
14077 mimetype : file.type,
14085 * addCard - add an Attachment to the uploader
14086 * @param data - the data about the image to upload
14090 title : "Title of file",
14091 is_uploaded : false,
14092 src : "http://.....",
14093 srcfile : { the File upload object },
14094 mimetype : file.type,
14097 .. any other data...
14103 addCard : function (data)
14105 // hidden input element?
14106 // if the file is not an image...
14107 //then we need to use something other that and header_image
14112 xns : Roo.bootstrap,
14113 xtype : 'CardFooter',
14116 xns : Roo.bootstrap,
14122 xns : Roo.bootstrap,
14124 html : String.format("<small>{0}</small>", data.title),
14125 cls : 'col-10 text-left',
14130 click : function() {
14132 t.fireEvent( "download", t, data );
14138 xns : Roo.bootstrap,
14140 style: 'max-height: 28px; ',
14146 click : function() {
14147 t.removeCard(data.id)
14159 var cn = this.addxtype(
14162 xns : Roo.bootstrap,
14165 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14166 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14167 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14172 initEvents : function() {
14173 Roo.bootstrap.Card.prototype.initEvents.call(this);
14175 this.imgEl = this.el.select('.card-img-top').first();
14177 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14178 this.imgEl.set({ 'pointer' : 'cursor' });
14181 this.getCardFooter().addClass('p-1');
14188 // dont' really need ot update items.
14189 // this.items.push(cn);
14190 this.fileCollection.add(cn);
14192 if (!data.srcfile) {
14193 this.updateInput();
14198 var reader = new FileReader();
14199 reader.addEventListener("load", function() {
14200 data.srcdata = reader.result;
14203 reader.readAsDataURL(data.srcfile);
14208 removeCard : function(id)
14211 var card = this.fileCollection.get(id);
14212 card.data.is_deleted = 1;
14213 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14214 //this.fileCollection.remove(card);
14215 //this.items = this.items.filter(function(e) { return e != card });
14216 // dont' really need ot update items.
14217 card.el.dom.parentNode.removeChild(card.el.dom);
14218 this.updateInput();
14224 this.fileCollection.each(function(card) {
14225 if (card.el.dom && card.el.dom.parentNode) {
14226 card.el.dom.parentNode.removeChild(card.el.dom);
14229 this.fileCollection.clear();
14230 this.updateInput();
14233 updateInput : function()
14236 this.fileCollection.each(function(e) {
14240 this.inputEl().dom.value = JSON.stringify(data);
14250 Roo.bootstrap.CardUploader.ID = -1;/*
14252 * Ext JS Library 1.1.1
14253 * Copyright(c) 2006-2007, Ext JS, LLC.
14255 * Originally Released Under LGPL - original licence link has changed is not relivant.
14258 * <script type="text/javascript">
14263 * @class Roo.data.SortTypes
14265 * Defines the default sorting (casting?) comparison functions used when sorting data.
14267 Roo.data.SortTypes = {
14269 * Default sort that does nothing
14270 * @param {Mixed} s The value being converted
14271 * @return {Mixed} The comparison value
14273 none : function(s){
14278 * The regular expression used to strip tags
14282 stripTagsRE : /<\/?[^>]+>/gi,
14285 * Strips all HTML tags to sort on text only
14286 * @param {Mixed} s The value being converted
14287 * @return {String} The comparison value
14289 asText : function(s){
14290 return String(s).replace(this.stripTagsRE, "");
14294 * Strips all HTML tags to sort on text only - Case insensitive
14295 * @param {Mixed} s The value being converted
14296 * @return {String} The comparison value
14298 asUCText : function(s){
14299 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14303 * Case insensitive string
14304 * @param {Mixed} s The value being converted
14305 * @return {String} The comparison value
14307 asUCString : function(s) {
14308 return String(s).toUpperCase();
14313 * @param {Mixed} s The value being converted
14314 * @return {Number} The comparison value
14316 asDate : function(s) {
14320 if(s instanceof Date){
14321 return s.getTime();
14323 return Date.parse(String(s));
14328 * @param {Mixed} s The value being converted
14329 * @return {Float} The comparison value
14331 asFloat : function(s) {
14332 var val = parseFloat(String(s).replace(/,/g, ""));
14341 * @param {Mixed} s The value being converted
14342 * @return {Number} The comparison value
14344 asInt : function(s) {
14345 var val = parseInt(String(s).replace(/,/g, ""));
14353 * Ext JS Library 1.1.1
14354 * Copyright(c) 2006-2007, Ext JS, LLC.
14356 * Originally Released Under LGPL - original licence link has changed is not relivant.
14359 * <script type="text/javascript">
14363 * @class Roo.data.Record
14364 * Instances of this class encapsulate both record <em>definition</em> information, and record
14365 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14366 * to access Records cached in an {@link Roo.data.Store} object.<br>
14368 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14369 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14372 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14374 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14375 * {@link #create}. The parameters are the same.
14376 * @param {Array} data An associative Array of data values keyed by the field name.
14377 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14378 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14379 * not specified an integer id is generated.
14381 Roo.data.Record = function(data, id){
14382 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14387 * Generate a constructor for a specific record layout.
14388 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14389 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14390 * Each field definition object may contain the following properties: <ul>
14391 * <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,
14392 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14393 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14394 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14395 * is being used, then this is a string containing the javascript expression to reference the data relative to
14396 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14397 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14398 * this may be omitted.</p></li>
14399 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14400 * <ul><li>auto (Default, implies no conversion)</li>
14405 * <li>date</li></ul></p></li>
14406 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14407 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14408 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14409 * by the Reader into an object that will be stored in the Record. It is passed the
14410 * following parameters:<ul>
14411 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14413 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14415 * <br>usage:<br><pre><code>
14416 var TopicRecord = Roo.data.Record.create(
14417 {name: 'title', mapping: 'topic_title'},
14418 {name: 'author', mapping: 'username'},
14419 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14420 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14421 {name: 'lastPoster', mapping: 'user2'},
14422 {name: 'excerpt', mapping: 'post_text'}
14425 var myNewRecord = new TopicRecord({
14426 title: 'Do my job please',
14429 lastPost: new Date(),
14430 lastPoster: 'Animal',
14431 excerpt: 'No way dude!'
14433 myStore.add(myNewRecord);
14438 Roo.data.Record.create = function(o){
14439 var f = function(){
14440 f.superclass.constructor.apply(this, arguments);
14442 Roo.extend(f, Roo.data.Record);
14443 var p = f.prototype;
14444 p.fields = new Roo.util.MixedCollection(false, function(field){
14447 for(var i = 0, len = o.length; i < len; i++){
14448 p.fields.add(new Roo.data.Field(o[i]));
14450 f.getField = function(name){
14451 return p.fields.get(name);
14456 Roo.data.Record.AUTO_ID = 1000;
14457 Roo.data.Record.EDIT = 'edit';
14458 Roo.data.Record.REJECT = 'reject';
14459 Roo.data.Record.COMMIT = 'commit';
14461 Roo.data.Record.prototype = {
14463 * Readonly flag - true if this record has been modified.
14472 join : function(store){
14473 this.store = store;
14477 * Set the named field to the specified value.
14478 * @param {String} name The name of the field to set.
14479 * @param {Object} value The value to set the field to.
14481 set : function(name, value){
14482 if(this.data[name] == value){
14486 if(!this.modified){
14487 this.modified = {};
14489 if(typeof this.modified[name] == 'undefined'){
14490 this.modified[name] = this.data[name];
14492 this.data[name] = value;
14493 if(!this.editing && this.store){
14494 this.store.afterEdit(this);
14499 * Get the value of the named field.
14500 * @param {String} name The name of the field to get the value of.
14501 * @return {Object} The value of the field.
14503 get : function(name){
14504 return this.data[name];
14508 beginEdit : function(){
14509 this.editing = true;
14510 this.modified = {};
14514 cancelEdit : function(){
14515 this.editing = false;
14516 delete this.modified;
14520 endEdit : function(){
14521 this.editing = false;
14522 if(this.dirty && this.store){
14523 this.store.afterEdit(this);
14528 * Usually called by the {@link Roo.data.Store} which owns the Record.
14529 * Rejects all changes made to the Record since either creation, or the last commit operation.
14530 * Modified fields are reverted to their original values.
14532 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14533 * of reject operations.
14535 reject : function(){
14536 var m = this.modified;
14538 if(typeof m[n] != "function"){
14539 this.data[n] = m[n];
14542 this.dirty = false;
14543 delete this.modified;
14544 this.editing = false;
14546 this.store.afterReject(this);
14551 * Usually called by the {@link Roo.data.Store} which owns the Record.
14552 * Commits all changes made to the Record since either creation, or the last commit operation.
14554 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14555 * of commit operations.
14557 commit : function(){
14558 this.dirty = false;
14559 delete this.modified;
14560 this.editing = false;
14562 this.store.afterCommit(this);
14567 hasError : function(){
14568 return this.error != null;
14572 clearError : function(){
14577 * Creates a copy of this record.
14578 * @param {String} id (optional) A new record id if you don't want to use this record's id
14581 copy : function(newId) {
14582 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14586 * Ext JS Library 1.1.1
14587 * Copyright(c) 2006-2007, Ext JS, LLC.
14589 * Originally Released Under LGPL - original licence link has changed is not relivant.
14592 * <script type="text/javascript">
14598 * @class Roo.data.Store
14599 * @extends Roo.util.Observable
14600 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14601 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14603 * 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
14604 * has no knowledge of the format of the data returned by the Proxy.<br>
14606 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14607 * instances from the data object. These records are cached and made available through accessor functions.
14609 * Creates a new Store.
14610 * @param {Object} config A config object containing the objects needed for the Store to access data,
14611 * and read the data into Records.
14613 Roo.data.Store = function(config){
14614 this.data = new Roo.util.MixedCollection(false);
14615 this.data.getKey = function(o){
14618 this.baseParams = {};
14620 this.paramNames = {
14625 "multisort" : "_multisort"
14628 if(config && config.data){
14629 this.inlineData = config.data;
14630 delete config.data;
14633 Roo.apply(this, config);
14635 if(this.reader){ // reader passed
14636 this.reader = Roo.factory(this.reader, Roo.data);
14637 this.reader.xmodule = this.xmodule || false;
14638 if(!this.recordType){
14639 this.recordType = this.reader.recordType;
14641 if(this.reader.onMetaChange){
14642 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14646 if(this.recordType){
14647 this.fields = this.recordType.prototype.fields;
14649 this.modified = [];
14653 * @event datachanged
14654 * Fires when the data cache has changed, and a widget which is using this Store
14655 * as a Record cache should refresh its view.
14656 * @param {Store} this
14658 datachanged : true,
14660 * @event metachange
14661 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14662 * @param {Store} this
14663 * @param {Object} meta The JSON metadata
14668 * Fires when Records have been added to the Store
14669 * @param {Store} this
14670 * @param {Roo.data.Record[]} records The array of Records added
14671 * @param {Number} index The index at which the record(s) were added
14676 * Fires when a Record has been removed from the Store
14677 * @param {Store} this
14678 * @param {Roo.data.Record} record The Record that was removed
14679 * @param {Number} index The index at which the record was removed
14684 * Fires when a Record has been updated
14685 * @param {Store} this
14686 * @param {Roo.data.Record} record The Record that was updated
14687 * @param {String} operation The update operation being performed. Value may be one of:
14689 Roo.data.Record.EDIT
14690 Roo.data.Record.REJECT
14691 Roo.data.Record.COMMIT
14697 * Fires when the data cache has been cleared.
14698 * @param {Store} this
14702 * @event beforeload
14703 * Fires before a request is made for a new data object. If the beforeload handler returns false
14704 * the load action will be canceled.
14705 * @param {Store} this
14706 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14710 * @event beforeloadadd
14711 * Fires after a new set of Records has been loaded.
14712 * @param {Store} this
14713 * @param {Roo.data.Record[]} records The Records that were loaded
14714 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14716 beforeloadadd : true,
14719 * Fires after a new set of Records has been loaded, before they are added to the store.
14720 * @param {Store} this
14721 * @param {Roo.data.Record[]} records The Records that were loaded
14722 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14723 * @params {Object} return from reader
14727 * @event loadexception
14728 * Fires if an exception occurs in the Proxy during loading.
14729 * Called with the signature of the Proxy's "loadexception" event.
14730 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14733 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14734 * @param {Object} load options
14735 * @param {Object} jsonData from your request (normally this contains the Exception)
14737 loadexception : true
14741 this.proxy = Roo.factory(this.proxy, Roo.data);
14742 this.proxy.xmodule = this.xmodule || false;
14743 this.relayEvents(this.proxy, ["loadexception"]);
14745 this.sortToggle = {};
14746 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14748 Roo.data.Store.superclass.constructor.call(this);
14750 if(this.inlineData){
14751 this.loadData(this.inlineData);
14752 delete this.inlineData;
14756 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14758 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14759 * without a remote query - used by combo/forms at present.
14763 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14766 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14769 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
14770 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14773 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14774 * on any HTTP request
14777 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14780 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14784 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14785 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14787 remoteSort : false,
14790 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14791 * loaded or when a record is removed. (defaults to false).
14793 pruneModifiedRecords : false,
14796 lastOptions : null,
14799 * Add Records to the Store and fires the add event.
14800 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14802 add : function(records){
14803 records = [].concat(records);
14804 for(var i = 0, len = records.length; i < len; i++){
14805 records[i].join(this);
14807 var index = this.data.length;
14808 this.data.addAll(records);
14809 this.fireEvent("add", this, records, index);
14813 * Remove a Record from the Store and fires the remove event.
14814 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14816 remove : function(record){
14817 var index = this.data.indexOf(record);
14818 this.data.removeAt(index);
14820 if(this.pruneModifiedRecords){
14821 this.modified.remove(record);
14823 this.fireEvent("remove", this, record, index);
14827 * Remove all Records from the Store and fires the clear event.
14829 removeAll : function(){
14831 if(this.pruneModifiedRecords){
14832 this.modified = [];
14834 this.fireEvent("clear", this);
14838 * Inserts Records to the Store at the given index and fires the add event.
14839 * @param {Number} index The start index at which to insert the passed Records.
14840 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14842 insert : function(index, records){
14843 records = [].concat(records);
14844 for(var i = 0, len = records.length; i < len; i++){
14845 this.data.insert(index, records[i]);
14846 records[i].join(this);
14848 this.fireEvent("add", this, records, index);
14852 * Get the index within the cache of the passed Record.
14853 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14854 * @return {Number} The index of the passed Record. Returns -1 if not found.
14856 indexOf : function(record){
14857 return this.data.indexOf(record);
14861 * Get the index within the cache of the Record with the passed id.
14862 * @param {String} id The id of the Record to find.
14863 * @return {Number} The index of the Record. Returns -1 if not found.
14865 indexOfId : function(id){
14866 return this.data.indexOfKey(id);
14870 * Get the Record with the specified id.
14871 * @param {String} id The id of the Record to find.
14872 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14874 getById : function(id){
14875 return this.data.key(id);
14879 * Get the Record at the specified index.
14880 * @param {Number} index The index of the Record to find.
14881 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14883 getAt : function(index){
14884 return this.data.itemAt(index);
14888 * Returns a range of Records between specified indices.
14889 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14890 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14891 * @return {Roo.data.Record[]} An array of Records
14893 getRange : function(start, end){
14894 return this.data.getRange(start, end);
14898 storeOptions : function(o){
14899 o = Roo.apply({}, o);
14902 this.lastOptions = o;
14906 * Loads the Record cache from the configured Proxy using the configured Reader.
14908 * If using remote paging, then the first load call must specify the <em>start</em>
14909 * and <em>limit</em> properties in the options.params property to establish the initial
14910 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14912 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14913 * and this call will return before the new data has been loaded. Perform any post-processing
14914 * in a callback function, or in a "load" event handler.</strong>
14916 * @param {Object} options An object containing properties which control loading options:<ul>
14917 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14918 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14919 * passed the following arguments:<ul>
14920 * <li>r : Roo.data.Record[]</li>
14921 * <li>options: Options object from the load call</li>
14922 * <li>success: Boolean success indicator</li></ul></li>
14923 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14924 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14927 load : function(options){
14928 options = options || {};
14929 if(this.fireEvent("beforeload", this, options) !== false){
14930 this.storeOptions(options);
14931 var p = Roo.apply(options.params || {}, this.baseParams);
14932 // if meta was not loaded from remote source.. try requesting it.
14933 if (!this.reader.metaFromRemote) {
14934 p._requestMeta = 1;
14936 if(this.sortInfo && this.remoteSort){
14937 var pn = this.paramNames;
14938 p[pn["sort"]] = this.sortInfo.field;
14939 p[pn["dir"]] = this.sortInfo.direction;
14941 if (this.multiSort) {
14942 var pn = this.paramNames;
14943 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14946 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14951 * Reloads the Record cache from the configured Proxy using the configured Reader and
14952 * the options from the last load operation performed.
14953 * @param {Object} options (optional) An object containing properties which may override the options
14954 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14955 * the most recently used options are reused).
14957 reload : function(options){
14958 this.load(Roo.applyIf(options||{}, this.lastOptions));
14962 // Called as a callback by the Reader during a load operation.
14963 loadRecords : function(o, options, success){
14964 if(!o || success === false){
14965 if(success !== false){
14966 this.fireEvent("load", this, [], options, o);
14968 if(options.callback){
14969 options.callback.call(options.scope || this, [], options, false);
14973 // if data returned failure - throw an exception.
14974 if (o.success === false) {
14975 // show a message if no listener is registered.
14976 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14977 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14979 // loadmask wil be hooked into this..
14980 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14983 var r = o.records, t = o.totalRecords || r.length;
14985 this.fireEvent("beforeloadadd", this, r, options, o);
14987 if(!options || options.add !== true){
14988 if(this.pruneModifiedRecords){
14989 this.modified = [];
14991 for(var i = 0, len = r.length; i < len; i++){
14995 this.data = this.snapshot;
14996 delete this.snapshot;
14999 this.data.addAll(r);
15000 this.totalLength = t;
15002 this.fireEvent("datachanged", this);
15004 this.totalLength = Math.max(t, this.data.length+r.length);
15008 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15010 var e = new Roo.data.Record({});
15012 e.set(this.parent.displayField, this.parent.emptyTitle);
15013 e.set(this.parent.valueField, '');
15018 this.fireEvent("load", this, r, options, o);
15019 if(options.callback){
15020 options.callback.call(options.scope || this, r, options, true);
15026 * Loads data from a passed data block. A Reader which understands the format of the data
15027 * must have been configured in the constructor.
15028 * @param {Object} data The data block from which to read the Records. The format of the data expected
15029 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15030 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15032 loadData : function(o, append){
15033 var r = this.reader.readRecords(o);
15034 this.loadRecords(r, {add: append}, true);
15038 * using 'cn' the nested child reader read the child array into it's child stores.
15039 * @param {Object} rec The record with a 'children array
15041 loadDataFromChildren : function(rec)
15043 this.loadData(this.reader.toLoadData(rec));
15048 * Gets the number of cached records.
15050 * <em>If using paging, this may not be the total size of the dataset. If the data object
15051 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15052 * the data set size</em>
15054 getCount : function(){
15055 return this.data.length || 0;
15059 * Gets the total number of records in the dataset as returned by the server.
15061 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15062 * the dataset size</em>
15064 getTotalCount : function(){
15065 return this.totalLength || 0;
15069 * Returns the sort state of the Store as an object with two properties:
15071 field {String} The name of the field by which the Records are sorted
15072 direction {String} The sort order, "ASC" or "DESC"
15075 getSortState : function(){
15076 return this.sortInfo;
15080 applySort : function(){
15081 if(this.sortInfo && !this.remoteSort){
15082 var s = this.sortInfo, f = s.field;
15083 var st = this.fields.get(f).sortType;
15084 var fn = function(r1, r2){
15085 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15086 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15088 this.data.sort(s.direction, fn);
15089 if(this.snapshot && this.snapshot != this.data){
15090 this.snapshot.sort(s.direction, fn);
15096 * Sets the default sort column and order to be used by the next load operation.
15097 * @param {String} fieldName The name of the field to sort by.
15098 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15100 setDefaultSort : function(field, dir){
15101 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15105 * Sort the Records.
15106 * If remote sorting is used, the sort is performed on the server, and the cache is
15107 * reloaded. If local sorting is used, the cache is sorted internally.
15108 * @param {String} fieldName The name of the field to sort by.
15109 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15111 sort : function(fieldName, dir){
15112 var f = this.fields.get(fieldName);
15114 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15116 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15117 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15122 this.sortToggle[f.name] = dir;
15123 this.sortInfo = {field: f.name, direction: dir};
15124 if(!this.remoteSort){
15126 this.fireEvent("datachanged", this);
15128 this.load(this.lastOptions);
15133 * Calls the specified function for each of the Records in the cache.
15134 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15135 * Returning <em>false</em> aborts and exits the iteration.
15136 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15138 each : function(fn, scope){
15139 this.data.each(fn, scope);
15143 * Gets all records modified since the last commit. Modified records are persisted across load operations
15144 * (e.g., during paging).
15145 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15147 getModifiedRecords : function(){
15148 return this.modified;
15152 createFilterFn : function(property, value, anyMatch){
15153 if(!value.exec){ // not a regex
15154 value = String(value);
15155 if(value.length == 0){
15158 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15160 return function(r){
15161 return value.test(r.data[property]);
15166 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15167 * @param {String} property A field on your records
15168 * @param {Number} start The record index to start at (defaults to 0)
15169 * @param {Number} end The last record index to include (defaults to length - 1)
15170 * @return {Number} The sum
15172 sum : function(property, start, end){
15173 var rs = this.data.items, v = 0;
15174 start = start || 0;
15175 end = (end || end === 0) ? end : rs.length-1;
15177 for(var i = start; i <= end; i++){
15178 v += (rs[i].data[property] || 0);
15184 * Filter the records by a specified property.
15185 * @param {String} field A field on your records
15186 * @param {String/RegExp} value Either a string that the field
15187 * should start with or a RegExp to test against the field
15188 * @param {Boolean} anyMatch True to match any part not just the beginning
15190 filter : function(property, value, anyMatch){
15191 var fn = this.createFilterFn(property, value, anyMatch);
15192 return fn ? this.filterBy(fn) : this.clearFilter();
15196 * Filter by a function. The specified function will be called with each
15197 * record in this data source. If the function returns true the record is included,
15198 * otherwise it is filtered.
15199 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15200 * @param {Object} scope (optional) The scope of the function (defaults to this)
15202 filterBy : function(fn, scope){
15203 this.snapshot = this.snapshot || this.data;
15204 this.data = this.queryBy(fn, scope||this);
15205 this.fireEvent("datachanged", this);
15209 * Query the records by a specified property.
15210 * @param {String} field A field on your records
15211 * @param {String/RegExp} value Either a string that the field
15212 * should start with or a RegExp to test against the field
15213 * @param {Boolean} anyMatch True to match any part not just the beginning
15214 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15216 query : function(property, value, anyMatch){
15217 var fn = this.createFilterFn(property, value, anyMatch);
15218 return fn ? this.queryBy(fn) : this.data.clone();
15222 * Query by a function. The specified function will be called with each
15223 * record in this data source. If the function returns true the record is included
15225 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15226 * @param {Object} scope (optional) The scope of the function (defaults to this)
15227 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15229 queryBy : function(fn, scope){
15230 var data = this.snapshot || this.data;
15231 return data.filterBy(fn, scope||this);
15235 * Collects unique values for a particular dataIndex from this store.
15236 * @param {String} dataIndex The property to collect
15237 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15238 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15239 * @return {Array} An array of the unique values
15241 collect : function(dataIndex, allowNull, bypassFilter){
15242 var d = (bypassFilter === true && this.snapshot) ?
15243 this.snapshot.items : this.data.items;
15244 var v, sv, r = [], l = {};
15245 for(var i = 0, len = d.length; i < len; i++){
15246 v = d[i].data[dataIndex];
15248 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15257 * Revert to a view of the Record cache with no filtering applied.
15258 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15260 clearFilter : function(suppressEvent){
15261 if(this.snapshot && this.snapshot != this.data){
15262 this.data = this.snapshot;
15263 delete this.snapshot;
15264 if(suppressEvent !== true){
15265 this.fireEvent("datachanged", this);
15271 afterEdit : function(record){
15272 if(this.modified.indexOf(record) == -1){
15273 this.modified.push(record);
15275 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15279 afterReject : function(record){
15280 this.modified.remove(record);
15281 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15285 afterCommit : function(record){
15286 this.modified.remove(record);
15287 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15291 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15292 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15294 commitChanges : function(){
15295 var m = this.modified.slice(0);
15296 this.modified = [];
15297 for(var i = 0, len = m.length; i < len; i++){
15303 * Cancel outstanding changes on all changed records.
15305 rejectChanges : function(){
15306 var m = this.modified.slice(0);
15307 this.modified = [];
15308 for(var i = 0, len = m.length; i < len; i++){
15313 onMetaChange : function(meta, rtype, o){
15314 this.recordType = rtype;
15315 this.fields = rtype.prototype.fields;
15316 delete this.snapshot;
15317 this.sortInfo = meta.sortInfo || this.sortInfo;
15318 this.modified = [];
15319 this.fireEvent('metachange', this, this.reader.meta);
15322 moveIndex : function(data, type)
15324 var index = this.indexOf(data);
15326 var newIndex = index + type;
15330 this.insert(newIndex, data);
15335 * Ext JS Library 1.1.1
15336 * Copyright(c) 2006-2007, Ext JS, LLC.
15338 * Originally Released Under LGPL - original licence link has changed is not relivant.
15341 * <script type="text/javascript">
15345 * @class Roo.data.SimpleStore
15346 * @extends Roo.data.Store
15347 * Small helper class to make creating Stores from Array data easier.
15348 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15349 * @cfg {Array} fields An array of field definition objects, or field name strings.
15350 * @cfg {Object} an existing reader (eg. copied from another store)
15351 * @cfg {Array} data The multi-dimensional array of data
15352 * @cfg {Roo.data.DataProxy} proxy [not-required]
15353 * @cfg {Roo.data.Reader} reader [not-required]
15355 * @param {Object} config
15357 Roo.data.SimpleStore = function(config)
15359 Roo.data.SimpleStore.superclass.constructor.call(this, {
15361 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15364 Roo.data.Record.create(config.fields)
15366 proxy : new Roo.data.MemoryProxy(config.data)
15370 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15372 * Ext JS Library 1.1.1
15373 * Copyright(c) 2006-2007, Ext JS, LLC.
15375 * Originally Released Under LGPL - original licence link has changed is not relivant.
15378 * <script type="text/javascript">
15383 * @extends Roo.data.Store
15384 * @class Roo.data.JsonStore
15385 * Small helper class to make creating Stores for JSON data easier. <br/>
15387 var store = new Roo.data.JsonStore({
15388 url: 'get-images.php',
15390 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15393 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15394 * JsonReader and HttpProxy (unless inline data is provided).</b>
15395 * @cfg {Array} fields An array of field definition objects, or field name strings.
15397 * @param {Object} config
15399 Roo.data.JsonStore = function(c){
15400 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15401 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15402 reader: new Roo.data.JsonReader(c, c.fields)
15405 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15407 * Ext JS Library 1.1.1
15408 * Copyright(c) 2006-2007, Ext JS, LLC.
15410 * Originally Released Under LGPL - original licence link has changed is not relivant.
15413 * <script type="text/javascript">
15417 Roo.data.Field = function(config){
15418 if(typeof config == "string"){
15419 config = {name: config};
15421 Roo.apply(this, config);
15424 this.type = "auto";
15427 var st = Roo.data.SortTypes;
15428 // named sortTypes are supported, here we look them up
15429 if(typeof this.sortType == "string"){
15430 this.sortType = st[this.sortType];
15433 // set default sortType for strings and dates
15434 if(!this.sortType){
15437 this.sortType = st.asUCString;
15440 this.sortType = st.asDate;
15443 this.sortType = st.none;
15448 var stripRe = /[\$,%]/g;
15450 // prebuilt conversion function for this field, instead of
15451 // switching every time we're reading a value
15453 var cv, dateFormat = this.dateFormat;
15458 cv = function(v){ return v; };
15461 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15465 return v !== undefined && v !== null && v !== '' ?
15466 parseInt(String(v).replace(stripRe, ""), 10) : '';
15471 return v !== undefined && v !== null && v !== '' ?
15472 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15477 cv = function(v){ return v === true || v === "true" || v == 1; };
15484 if(v instanceof Date){
15488 if(dateFormat == "timestamp"){
15489 return new Date(v*1000);
15491 return Date.parseDate(v, dateFormat);
15493 var parsed = Date.parse(v);
15494 return parsed ? new Date(parsed) : null;
15503 Roo.data.Field.prototype = {
15511 * Ext JS Library 1.1.1
15512 * Copyright(c) 2006-2007, Ext JS, LLC.
15514 * Originally Released Under LGPL - original licence link has changed is not relivant.
15517 * <script type="text/javascript">
15520 // Base class for reading structured data from a data source. This class is intended to be
15521 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15524 * @class Roo.data.DataReader
15526 * Base class for reading structured data from a data source. This class is intended to be
15527 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15530 Roo.data.DataReader = function(meta, recordType){
15534 this.recordType = recordType instanceof Array ?
15535 Roo.data.Record.create(recordType) : recordType;
15538 Roo.data.DataReader.prototype = {
15541 readerType : 'Data',
15543 * Create an empty record
15544 * @param {Object} data (optional) - overlay some values
15545 * @return {Roo.data.Record} record created.
15547 newRow : function(d) {
15549 this.recordType.prototype.fields.each(function(c) {
15551 case 'int' : da[c.name] = 0; break;
15552 case 'date' : da[c.name] = new Date(); break;
15553 case 'float' : da[c.name] = 0.0; break;
15554 case 'boolean' : da[c.name] = false; break;
15555 default : da[c.name] = ""; break;
15559 return new this.recordType(Roo.apply(da, d));
15565 * Ext JS Library 1.1.1
15566 * Copyright(c) 2006-2007, Ext JS, LLC.
15568 * Originally Released Under LGPL - original licence link has changed is not relivant.
15571 * <script type="text/javascript">
15575 * @class Roo.data.DataProxy
15576 * @extends Roo.data.Observable
15578 * This class is an abstract base class for implementations which provide retrieval of
15579 * unformatted data objects.<br>
15581 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15582 * (of the appropriate type which knows how to parse the data object) to provide a block of
15583 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15585 * Custom implementations must implement the load method as described in
15586 * {@link Roo.data.HttpProxy#load}.
15588 Roo.data.DataProxy = function(){
15591 * @event beforeload
15592 * Fires before a network request is made to retrieve a data object.
15593 * @param {Object} This DataProxy object.
15594 * @param {Object} params The params parameter to the load function.
15599 * Fires before the load method's callback is called.
15600 * @param {Object} This DataProxy object.
15601 * @param {Object} o The data object.
15602 * @param {Object} arg The callback argument object passed to the load function.
15606 * @event loadexception
15607 * Fires if an Exception occurs during data retrieval.
15608 * @param {Object} This DataProxy object.
15609 * @param {Object} o The data object.
15610 * @param {Object} arg The callback argument object passed to the load function.
15611 * @param {Object} e The Exception.
15613 loadexception : true
15615 Roo.data.DataProxy.superclass.constructor.call(this);
15618 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15621 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15625 * Ext JS Library 1.1.1
15626 * Copyright(c) 2006-2007, Ext JS, LLC.
15628 * Originally Released Under LGPL - original licence link has changed is not relivant.
15631 * <script type="text/javascript">
15634 * @class Roo.data.MemoryProxy
15635 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15636 * to the Reader when its load method is called.
15638 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15640 Roo.data.MemoryProxy = function(data){
15644 Roo.data.MemoryProxy.superclass.constructor.call(this);
15648 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15651 * Load data from the requested source (in this case an in-memory
15652 * data object passed to the constructor), read the data object into
15653 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15654 * process that block using the passed callback.
15655 * @param {Object} params This parameter is not used by the MemoryProxy class.
15656 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15657 * object into a block of Roo.data.Records.
15658 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15659 * The function must be passed <ul>
15660 * <li>The Record block object</li>
15661 * <li>The "arg" argument from the load function</li>
15662 * <li>A boolean success indicator</li>
15664 * @param {Object} scope The scope in which to call the callback
15665 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15667 load : function(params, reader, callback, scope, arg){
15668 params = params || {};
15671 result = reader.readRecords(params.data ? params.data :this.data);
15673 this.fireEvent("loadexception", this, arg, null, e);
15674 callback.call(scope, null, arg, false);
15677 callback.call(scope, result, arg, true);
15681 update : function(params, records){
15686 * Ext JS Library 1.1.1
15687 * Copyright(c) 2006-2007, Ext JS, LLC.
15689 * Originally Released Under LGPL - original licence link has changed is not relivant.
15692 * <script type="text/javascript">
15695 * @class Roo.data.HttpProxy
15696 * @extends Roo.data.DataProxy
15697 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15698 * configured to reference a certain URL.<br><br>
15700 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15701 * from which the running page was served.<br><br>
15703 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15705 * Be aware that to enable the browser to parse an XML document, the server must set
15706 * the Content-Type header in the HTTP response to "text/xml".
15708 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15709 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15710 * will be used to make the request.
15712 Roo.data.HttpProxy = function(conn){
15713 Roo.data.HttpProxy.superclass.constructor.call(this);
15714 // is conn a conn config or a real conn?
15716 this.useAjax = !conn || !conn.events;
15720 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15721 // thse are take from connection...
15724 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15727 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15728 * extra parameters to each request made by this object. (defaults to undefined)
15731 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15732 * to each request made by this object. (defaults to undefined)
15735 * @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)
15738 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15741 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15747 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15751 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15752 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15753 * a finer-grained basis than the DataProxy events.
15755 getConnection : function(){
15756 return this.useAjax ? Roo.Ajax : this.conn;
15760 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15761 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15762 * process that block using the passed callback.
15763 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15764 * for the request to the remote server.
15765 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15766 * object into a block of Roo.data.Records.
15767 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15768 * The function must be passed <ul>
15769 * <li>The Record block object</li>
15770 * <li>The "arg" argument from the load function</li>
15771 * <li>A boolean success indicator</li>
15773 * @param {Object} scope The scope in which to call the callback
15774 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15776 load : function(params, reader, callback, scope, arg){
15777 if(this.fireEvent("beforeload", this, params) !== false){
15779 params : params || {},
15781 callback : callback,
15786 callback : this.loadResponse,
15790 Roo.applyIf(o, this.conn);
15791 if(this.activeRequest){
15792 Roo.Ajax.abort(this.activeRequest);
15794 this.activeRequest = Roo.Ajax.request(o);
15796 this.conn.request(o);
15799 callback.call(scope||this, null, arg, false);
15804 loadResponse : function(o, success, response){
15805 delete this.activeRequest;
15807 this.fireEvent("loadexception", this, o, response);
15808 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15813 result = o.reader.read(response);
15815 this.fireEvent("loadexception", this, o, response, e);
15816 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15820 this.fireEvent("load", this, o, o.request.arg);
15821 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15825 update : function(dataSet){
15830 updateResponse : function(dataSet){
15835 * Ext JS Library 1.1.1
15836 * Copyright(c) 2006-2007, Ext JS, LLC.
15838 * Originally Released Under LGPL - original licence link has changed is not relivant.
15841 * <script type="text/javascript">
15845 * @class Roo.data.ScriptTagProxy
15846 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15847 * other than the originating domain of the running page.<br><br>
15849 * <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
15850 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15852 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15853 * source code that is used as the source inside a <script> tag.<br><br>
15855 * In order for the browser to process the returned data, the server must wrap the data object
15856 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15857 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15858 * depending on whether the callback name was passed:
15861 boolean scriptTag = false;
15862 String cb = request.getParameter("callback");
15865 response.setContentType("text/javascript");
15867 response.setContentType("application/x-json");
15869 Writer out = response.getWriter();
15871 out.write(cb + "(");
15873 out.print(dataBlock.toJsonString());
15880 * @param {Object} config A configuration object.
15882 Roo.data.ScriptTagProxy = function(config){
15883 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15884 Roo.apply(this, config);
15885 this.head = document.getElementsByTagName("head")[0];
15888 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15890 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15892 * @cfg {String} url The URL from which to request the data object.
15895 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15899 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15900 * the server the name of the callback function set up by the load call to process the returned data object.
15901 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15902 * javascript output which calls this named function passing the data object as its only parameter.
15904 callbackParam : "callback",
15906 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15907 * name to the request.
15912 * Load data from the configured URL, read the data object into
15913 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15914 * process that block using the passed callback.
15915 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15916 * for the request to the remote server.
15917 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15918 * object into a block of Roo.data.Records.
15919 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15920 * The function must be passed <ul>
15921 * <li>The Record block object</li>
15922 * <li>The "arg" argument from the load function</li>
15923 * <li>A boolean success indicator</li>
15925 * @param {Object} scope The scope in which to call the callback
15926 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15928 load : function(params, reader, callback, scope, arg){
15929 if(this.fireEvent("beforeload", this, params) !== false){
15931 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15933 var url = this.url;
15934 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15936 url += "&_dc=" + (new Date().getTime());
15938 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15941 cb : "stcCallback"+transId,
15942 scriptId : "stcScript"+transId,
15946 callback : callback,
15952 window[trans.cb] = function(o){
15953 conn.handleResponse(o, trans);
15956 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15958 if(this.autoAbort !== false){
15962 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15964 var script = document.createElement("script");
15965 script.setAttribute("src", url);
15966 script.setAttribute("type", "text/javascript");
15967 script.setAttribute("id", trans.scriptId);
15968 this.head.appendChild(script);
15970 this.trans = trans;
15972 callback.call(scope||this, null, arg, false);
15977 isLoading : function(){
15978 return this.trans ? true : false;
15982 * Abort the current server request.
15984 abort : function(){
15985 if(this.isLoading()){
15986 this.destroyTrans(this.trans);
15991 destroyTrans : function(trans, isLoaded){
15992 this.head.removeChild(document.getElementById(trans.scriptId));
15993 clearTimeout(trans.timeoutId);
15995 window[trans.cb] = undefined;
15997 delete window[trans.cb];
16000 // if hasn't been loaded, wait for load to remove it to prevent script error
16001 window[trans.cb] = function(){
16002 window[trans.cb] = undefined;
16004 delete window[trans.cb];
16011 handleResponse : function(o, trans){
16012 this.trans = false;
16013 this.destroyTrans(trans, true);
16016 result = trans.reader.readRecords(o);
16018 this.fireEvent("loadexception", this, o, trans.arg, e);
16019 trans.callback.call(trans.scope||window, null, trans.arg, false);
16022 this.fireEvent("load", this, o, trans.arg);
16023 trans.callback.call(trans.scope||window, result, trans.arg, true);
16027 handleFailure : function(trans){
16028 this.trans = false;
16029 this.destroyTrans(trans, false);
16030 this.fireEvent("loadexception", this, null, trans.arg);
16031 trans.callback.call(trans.scope||window, null, trans.arg, false);
16035 * Ext JS Library 1.1.1
16036 * Copyright(c) 2006-2007, Ext JS, LLC.
16038 * Originally Released Under LGPL - original licence link has changed is not relivant.
16041 * <script type="text/javascript">
16045 * @class Roo.data.JsonReader
16046 * @extends Roo.data.DataReader
16047 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16048 * based on mappings in a provided Roo.data.Record constructor.
16050 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16051 * in the reply previously.
16056 var RecordDef = Roo.data.Record.create([
16057 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16058 {name: 'occupation'} // This field will use "occupation" as the mapping.
16060 var myReader = new Roo.data.JsonReader({
16061 totalProperty: "results", // The property which contains the total dataset size (optional)
16062 root: "rows", // The property which contains an Array of row objects
16063 id: "id" // The property within each row object that provides an ID for the record (optional)
16067 * This would consume a JSON file like this:
16069 { 'results': 2, 'rows': [
16070 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16071 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16074 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16075 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16076 * paged from the remote server.
16077 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16078 * @cfg {String} root name of the property which contains the Array of row objects.
16079 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16080 * @cfg {Array} fields Array of field definition objects
16082 * Create a new JsonReader
16083 * @param {Object} meta Metadata configuration options
16084 * @param {Object} recordType Either an Array of field definition objects,
16085 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16087 Roo.data.JsonReader = function(meta, recordType){
16090 // set some defaults:
16091 Roo.applyIf(meta, {
16092 totalProperty: 'total',
16093 successProperty : 'success',
16098 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16100 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16102 readerType : 'Json',
16105 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16106 * Used by Store query builder to append _requestMeta to params.
16109 metaFromRemote : false,
16111 * This method is only used by a DataProxy which has retrieved data from a remote server.
16112 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16113 * @return {Object} data A data block which is used by an Roo.data.Store object as
16114 * a cache of Roo.data.Records.
16116 read : function(response){
16117 var json = response.responseText;
16119 var o = /* eval:var:o */ eval("("+json+")");
16121 throw {message: "JsonReader.read: Json object not found"};
16127 this.metaFromRemote = true;
16128 this.meta = o.metaData;
16129 this.recordType = Roo.data.Record.create(o.metaData.fields);
16130 this.onMetaChange(this.meta, this.recordType, o);
16132 return this.readRecords(o);
16135 // private function a store will implement
16136 onMetaChange : function(meta, recordType, o){
16143 simpleAccess: function(obj, subsc) {
16150 getJsonAccessor: function(){
16152 return function(expr) {
16154 return(re.test(expr))
16155 ? new Function("obj", "return obj." + expr)
16160 return Roo.emptyFn;
16165 * Create a data block containing Roo.data.Records from an XML document.
16166 * @param {Object} o An object which contains an Array of row objects in the property specified
16167 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16168 * which contains the total size of the dataset.
16169 * @return {Object} data A data block which is used by an Roo.data.Store object as
16170 * a cache of Roo.data.Records.
16172 readRecords : function(o){
16174 * After any data loads, the raw JSON data is available for further custom processing.
16178 var s = this.meta, Record = this.recordType,
16179 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16181 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16183 if(s.totalProperty) {
16184 this.getTotal = this.getJsonAccessor(s.totalProperty);
16186 if(s.successProperty) {
16187 this.getSuccess = this.getJsonAccessor(s.successProperty);
16189 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16191 var g = this.getJsonAccessor(s.id);
16192 this.getId = function(rec) {
16194 return (r === undefined || r === "") ? null : r;
16197 this.getId = function(){return null;};
16200 for(var jj = 0; jj < fl; jj++){
16202 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16203 this.ef[jj] = this.getJsonAccessor(map);
16207 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16208 if(s.totalProperty){
16209 var vt = parseInt(this.getTotal(o), 10);
16214 if(s.successProperty){
16215 var vs = this.getSuccess(o);
16216 if(vs === false || vs === 'false'){
16221 for(var i = 0; i < c; i++){
16224 var id = this.getId(n);
16225 for(var j = 0; j < fl; j++){
16227 var v = this.ef[j](n);
16229 Roo.log('missing convert for ' + f.name);
16233 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16235 var record = new Record(values, id);
16237 records[i] = record;
16243 totalRecords : totalRecords
16246 // used when loading children.. @see loadDataFromChildren
16247 toLoadData: function(rec)
16249 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16250 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16251 return { data : data, total : data.length };
16256 * Ext JS Library 1.1.1
16257 * Copyright(c) 2006-2007, Ext JS, LLC.
16259 * Originally Released Under LGPL - original licence link has changed is not relivant.
16262 * <script type="text/javascript">
16266 * @class Roo.data.ArrayReader
16267 * @extends Roo.data.DataReader
16268 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16269 * Each element of that Array represents a row of data fields. The
16270 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16271 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16275 var RecordDef = Roo.data.Record.create([
16276 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16277 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16279 var myReader = new Roo.data.ArrayReader({
16280 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16284 * This would consume an Array like this:
16286 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16290 * Create a new JsonReader
16291 * @param {Object} meta Metadata configuration options.
16292 * @param {Object|Array} recordType Either an Array of field definition objects
16294 * @cfg {Array} fields Array of field definition objects
16295 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16296 * as specified to {@link Roo.data.Record#create},
16297 * or an {@link Roo.data.Record} object
16300 * created using {@link Roo.data.Record#create}.
16302 Roo.data.ArrayReader = function(meta, recordType)
16304 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16307 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16310 * Create a data block containing Roo.data.Records from an XML document.
16311 * @param {Object} o An Array of row objects which represents the dataset.
16312 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16313 * a cache of Roo.data.Records.
16315 readRecords : function(o)
16317 var sid = this.meta ? this.meta.id : null;
16318 var recordType = this.recordType, fields = recordType.prototype.fields;
16321 for(var i = 0; i < root.length; i++){
16324 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16325 for(var j = 0, jlen = fields.length; j < jlen; j++){
16326 var f = fields.items[j];
16327 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16328 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16330 values[f.name] = v;
16332 var record = new recordType(values, id);
16334 records[records.length] = record;
16338 totalRecords : records.length
16341 // used when loading children.. @see loadDataFromChildren
16342 toLoadData: function(rec)
16344 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16345 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16356 * @class Roo.bootstrap.ComboBox
16357 * @extends Roo.bootstrap.TriggerField
16358 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16359 * @cfg {Boolean} append (true|false) default false
16360 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16361 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16362 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16363 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16364 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16365 * @cfg {Boolean} animate default true
16366 * @cfg {Boolean} emptyResultText only for touch device
16367 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16368 * @cfg {String} emptyTitle default ''
16369 * @cfg {Number} width fixed with? experimental
16371 * Create a new ComboBox.
16372 * @param {Object} config Configuration options
16374 Roo.bootstrap.ComboBox = function(config){
16375 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16379 * Fires when the dropdown list is expanded
16380 * @param {Roo.bootstrap.ComboBox} combo This combo box
16385 * Fires when the dropdown list is collapsed
16386 * @param {Roo.bootstrap.ComboBox} combo This combo box
16390 * @event beforeselect
16391 * Fires before a list item is selected. Return false to cancel the selection.
16392 * @param {Roo.bootstrap.ComboBox} combo This combo box
16393 * @param {Roo.data.Record} record The data record returned from the underlying store
16394 * @param {Number} index The index of the selected item in the dropdown list
16396 'beforeselect' : true,
16399 * Fires when a list item is selected
16400 * @param {Roo.bootstrap.ComboBox} combo This combo box
16401 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16402 * @param {Number} index The index of the selected item in the dropdown list
16406 * @event beforequery
16407 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16408 * The event object passed has these properties:
16409 * @param {Roo.bootstrap.ComboBox} combo This combo box
16410 * @param {String} query The query
16411 * @param {Boolean} forceAll true to force "all" query
16412 * @param {Boolean} cancel true to cancel the query
16413 * @param {Object} e The query event object
16415 'beforequery': true,
16418 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16419 * @param {Roo.bootstrap.ComboBox} combo This combo box
16424 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16425 * @param {Roo.bootstrap.ComboBox} combo This combo box
16426 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16431 * Fires when the remove value from the combobox array
16432 * @param {Roo.bootstrap.ComboBox} combo This combo box
16436 * @event afterremove
16437 * Fires when the remove value from the combobox array
16438 * @param {Roo.bootstrap.ComboBox} combo This combo box
16440 'afterremove' : true,
16442 * @event specialfilter
16443 * Fires when specialfilter
16444 * @param {Roo.bootstrap.ComboBox} combo This combo box
16446 'specialfilter' : true,
16449 * Fires when tick the element
16450 * @param {Roo.bootstrap.ComboBox} combo This combo box
16454 * @event touchviewdisplay
16455 * Fires when touch view require special display (default is using displayField)
16456 * @param {Roo.bootstrap.ComboBox} combo This combo box
16457 * @param {Object} cfg set html .
16459 'touchviewdisplay' : true
16464 this.tickItems = [];
16466 this.selectedIndex = -1;
16467 if(this.mode == 'local'){
16468 if(config.queryDelay === undefined){
16469 this.queryDelay = 10;
16471 if(config.minChars === undefined){
16477 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16480 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16481 * rendering into an Roo.Editor, defaults to false)
16484 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16485 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16488 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16491 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16492 * the dropdown list (defaults to undefined, with no header element)
16496 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16500 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16502 listWidth: undefined,
16504 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16505 * mode = 'remote' or 'text' if mode = 'local')
16507 displayField: undefined,
16510 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16511 * mode = 'remote' or 'value' if mode = 'local').
16512 * Note: use of a valueField requires the user make a selection
16513 * in order for a value to be mapped.
16515 valueField: undefined,
16517 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16522 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16523 * field's data value (defaults to the underlying DOM element's name)
16525 hiddenName: undefined,
16527 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16531 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16533 selectedClass: 'active',
16536 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16540 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16541 * anchor positions (defaults to 'tl-bl')
16543 listAlign: 'tl-bl?',
16545 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16549 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16550 * query specified by the allQuery config option (defaults to 'query')
16552 triggerAction: 'query',
16554 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16555 * (defaults to 4, does not apply if editable = false)
16559 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16560 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16564 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16565 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16569 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16570 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16574 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16575 * when editable = true (defaults to false)
16577 selectOnFocus:false,
16579 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16581 queryParam: 'query',
16583 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16584 * when mode = 'remote' (defaults to 'Loading...')
16586 loadingText: 'Loading...',
16588 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16592 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16596 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16597 * traditional select (defaults to true)
16601 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16605 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16609 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16610 * listWidth has a higher value)
16614 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16615 * allow the user to set arbitrary text into the field (defaults to false)
16617 forceSelection:false,
16619 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16620 * if typeAhead = true (defaults to 250)
16622 typeAheadDelay : 250,
16624 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16625 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16627 valueNotFoundText : undefined,
16629 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16631 blockFocus : false,
16634 * @cfg {Boolean} disableClear Disable showing of clear button.
16636 disableClear : false,
16638 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16640 alwaysQuery : false,
16643 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16648 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16650 invalidClass : "has-warning",
16653 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16655 validClass : "has-success",
16658 * @cfg {Boolean} specialFilter (true|false) special filter default false
16660 specialFilter : false,
16663 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16665 mobileTouchView : true,
16668 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16670 useNativeIOS : false,
16673 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16675 mobile_restrict_height : false,
16677 ios_options : false,
16689 btnPosition : 'right',
16690 triggerList : true,
16691 showToggleBtn : true,
16693 emptyResultText: 'Empty',
16694 triggerText : 'Select',
16698 // element that contains real text value.. (when hidden is used..)
16700 getAutoCreate : function()
16705 * Render classic select for iso
16708 if(Roo.isIOS && this.useNativeIOS){
16709 cfg = this.getAutoCreateNativeIOS();
16717 if(Roo.isTouch && this.mobileTouchView){
16718 cfg = this.getAutoCreateTouchView();
16725 if(!this.tickable){
16726 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16731 * ComboBox with tickable selections
16734 var align = this.labelAlign || this.parentLabelAlign();
16737 cls : 'form-group roo-combobox-tickable' //input-group
16740 var btn_text_select = '';
16741 var btn_text_done = '';
16742 var btn_text_cancel = '';
16744 if (this.btn_text_show) {
16745 btn_text_select = 'Select';
16746 btn_text_done = 'Done';
16747 btn_text_cancel = 'Cancel';
16752 cls : 'tickable-buttons',
16757 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16758 //html : this.triggerText
16759 html: btn_text_select
16765 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16767 html: btn_text_done
16773 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16775 html: btn_text_cancel
16781 buttons.cn.unshift({
16783 cls: 'roo-select2-search-field-input'
16789 Roo.each(buttons.cn, function(c){
16791 c.cls += ' btn-' + _this.size;
16794 if (_this.disabled) {
16801 style : 'display: contents',
16806 cls: 'form-hidden-field'
16810 cls: 'roo-select2-choices',
16814 cls: 'roo-select2-search-field',
16825 cls: 'roo-select2-container input-group roo-select2-container-multi',
16831 // cls: 'typeahead typeahead-long dropdown-menu',
16832 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16837 if(this.hasFeedback && !this.allowBlank){
16841 cls: 'glyphicon form-control-feedback'
16844 combobox.cn.push(feedback);
16851 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16852 tooltip : 'This field is required'
16854 if (Roo.bootstrap.version == 4) {
16857 style : 'display:none'
16860 if (align ==='left' && this.fieldLabel.length) {
16862 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16869 cls : 'control-label col-form-label',
16870 html : this.fieldLabel
16882 var labelCfg = cfg.cn[1];
16883 var contentCfg = cfg.cn[2];
16886 if(this.indicatorpos == 'right'){
16892 cls : 'control-label col-form-label',
16896 html : this.fieldLabel
16912 labelCfg = cfg.cn[0];
16913 contentCfg = cfg.cn[1];
16917 if(this.labelWidth > 12){
16918 labelCfg.style = "width: " + this.labelWidth + 'px';
16920 if(this.width * 1 > 0){
16921 contentCfg.style = "width: " + this.width + 'px';
16923 if(this.labelWidth < 13 && this.labelmd == 0){
16924 this.labelmd = this.labelWidth;
16927 if(this.labellg > 0){
16928 labelCfg.cls += ' col-lg-' + this.labellg;
16929 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16932 if(this.labelmd > 0){
16933 labelCfg.cls += ' col-md-' + this.labelmd;
16934 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16937 if(this.labelsm > 0){
16938 labelCfg.cls += ' col-sm-' + this.labelsm;
16939 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16942 if(this.labelxs > 0){
16943 labelCfg.cls += ' col-xs-' + this.labelxs;
16944 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16948 } else if ( this.fieldLabel.length) {
16949 // Roo.log(" label");
16954 //cls : 'input-group-addon',
16955 html : this.fieldLabel
16960 if(this.indicatorpos == 'right'){
16964 //cls : 'input-group-addon',
16965 html : this.fieldLabel
16975 // Roo.log(" no label && no align");
16982 ['xs','sm','md','lg'].map(function(size){
16983 if (settings[size]) {
16984 cfg.cls += ' col-' + size + '-' + settings[size];
16992 _initEventsCalled : false,
16995 initEvents: function()
16997 if (this._initEventsCalled) { // as we call render... prevent looping...
17000 this._initEventsCalled = true;
17003 throw "can not find store for combo";
17006 this.indicator = this.indicatorEl();
17008 this.store = Roo.factory(this.store, Roo.data);
17009 this.store.parent = this;
17011 // if we are building from html. then this element is so complex, that we can not really
17012 // use the rendered HTML.
17013 // so we have to trash and replace the previous code.
17014 if (Roo.XComponent.build_from_html) {
17015 // remove this element....
17016 var e = this.el.dom, k=0;
17017 while (e ) { e = e.previousSibling; ++k;}
17022 this.rendered = false;
17024 this.render(this.parent().getChildContainer(true), k);
17027 if(Roo.isIOS && this.useNativeIOS){
17028 this.initIOSView();
17036 if(Roo.isTouch && this.mobileTouchView){
17037 this.initTouchView();
17042 this.initTickableEvents();
17046 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17048 if(this.hiddenName){
17050 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17052 this.hiddenField.dom.value =
17053 this.hiddenValue !== undefined ? this.hiddenValue :
17054 this.value !== undefined ? this.value : '';
17056 // prevent input submission
17057 this.el.dom.removeAttribute('name');
17058 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17063 // this.el.dom.setAttribute('autocomplete', 'off');
17066 var cls = 'x-combo-list';
17068 //this.list = new Roo.Layer({
17069 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17075 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17076 _this.list.setWidth(lw);
17079 this.list.on('mouseover', this.onViewOver, this);
17080 this.list.on('mousemove', this.onViewMove, this);
17081 this.list.on('scroll', this.onViewScroll, this);
17084 this.list.swallowEvent('mousewheel');
17085 this.assetHeight = 0;
17088 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17089 this.assetHeight += this.header.getHeight();
17092 this.innerList = this.list.createChild({cls:cls+'-inner'});
17093 this.innerList.on('mouseover', this.onViewOver, this);
17094 this.innerList.on('mousemove', this.onViewMove, this);
17095 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17097 if(this.allowBlank && !this.pageSize && !this.disableClear){
17098 this.footer = this.list.createChild({cls:cls+'-ft'});
17099 this.pageTb = new Roo.Toolbar(this.footer);
17103 this.footer = this.list.createChild({cls:cls+'-ft'});
17104 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17105 {pageSize: this.pageSize});
17109 if (this.pageTb && this.allowBlank && !this.disableClear) {
17111 this.pageTb.add(new Roo.Toolbar.Fill(), {
17112 cls: 'x-btn-icon x-btn-clear',
17114 handler: function()
17117 _this.clearValue();
17118 _this.onSelect(false, -1);
17123 this.assetHeight += this.footer.getHeight();
17128 this.tpl = Roo.bootstrap.version == 4 ?
17129 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17130 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17133 this.view = new Roo.View(this.list, this.tpl, {
17134 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17136 //this.view.wrapEl.setDisplayed(false);
17137 this.view.on('click', this.onViewClick, this);
17140 this.store.on('beforeload', this.onBeforeLoad, this);
17141 this.store.on('load', this.onLoad, this);
17142 this.store.on('loadexception', this.onLoadException, this);
17144 if(this.resizable){
17145 this.resizer = new Roo.Resizable(this.list, {
17146 pinned:true, handles:'se'
17148 this.resizer.on('resize', function(r, w, h){
17149 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17150 this.listWidth = w;
17151 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17152 this.restrictHeight();
17154 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17157 if(!this.editable){
17158 this.editable = true;
17159 this.setEditable(false);
17164 if (typeof(this.events.add.listeners) != 'undefined') {
17166 this.addicon = this.wrap.createChild(
17167 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17169 this.addicon.on('click', function(e) {
17170 this.fireEvent('add', this);
17173 if (typeof(this.events.edit.listeners) != 'undefined') {
17175 this.editicon = this.wrap.createChild(
17176 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17177 if (this.addicon) {
17178 this.editicon.setStyle('margin-left', '40px');
17180 this.editicon.on('click', function(e) {
17182 // we fire even if inothing is selected..
17183 this.fireEvent('edit', this, this.lastData );
17189 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17190 "up" : function(e){
17191 this.inKeyMode = true;
17195 "down" : function(e){
17196 if(!this.isExpanded()){
17197 this.onTriggerClick();
17199 this.inKeyMode = true;
17204 "enter" : function(e){
17205 // this.onViewClick();
17209 if(this.fireEvent("specialkey", this, e)){
17210 this.onViewClick(false);
17216 "esc" : function(e){
17220 "tab" : function(e){
17223 if(this.fireEvent("specialkey", this, e)){
17224 this.onViewClick(false);
17232 doRelay : function(foo, bar, hname){
17233 if(hname == 'down' || this.scope.isExpanded()){
17234 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17243 this.queryDelay = Math.max(this.queryDelay || 10,
17244 this.mode == 'local' ? 10 : 250);
17247 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17249 if(this.typeAhead){
17250 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17252 if(this.editable !== false){
17253 this.inputEl().on("keyup", this.onKeyUp, this);
17255 if(this.forceSelection){
17256 this.inputEl().on('blur', this.doForce, this);
17260 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17261 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17265 initTickableEvents: function()
17269 if(this.hiddenName){
17271 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17273 this.hiddenField.dom.value =
17274 this.hiddenValue !== undefined ? this.hiddenValue :
17275 this.value !== undefined ? this.value : '';
17277 // prevent input submission
17278 this.el.dom.removeAttribute('name');
17279 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17284 // this.list = this.el.select('ul.dropdown-menu',true).first();
17286 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17287 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17288 if(this.triggerList){
17289 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17292 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17293 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17295 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17296 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17298 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17299 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17301 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17302 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17303 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17306 this.cancelBtn.hide();
17311 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17312 _this.list.setWidth(lw);
17315 this.list.on('mouseover', this.onViewOver, this);
17316 this.list.on('mousemove', this.onViewMove, this);
17318 this.list.on('scroll', this.onViewScroll, this);
17321 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17322 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17325 this.view = new Roo.View(this.list, this.tpl, {
17330 selectedClass: this.selectedClass
17333 //this.view.wrapEl.setDisplayed(false);
17334 this.view.on('click', this.onViewClick, this);
17338 this.store.on('beforeload', this.onBeforeLoad, this);
17339 this.store.on('load', this.onLoad, this);
17340 this.store.on('loadexception', this.onLoadException, this);
17343 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17344 "up" : function(e){
17345 this.inKeyMode = true;
17349 "down" : function(e){
17350 this.inKeyMode = true;
17354 "enter" : function(e){
17355 if(this.fireEvent("specialkey", this, e)){
17356 this.onViewClick(false);
17362 "esc" : function(e){
17363 this.onTickableFooterButtonClick(e, false, false);
17366 "tab" : function(e){
17367 this.fireEvent("specialkey", this, e);
17369 this.onTickableFooterButtonClick(e, false, false);
17376 doRelay : function(e, fn, key){
17377 if(this.scope.isExpanded()){
17378 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17387 this.queryDelay = Math.max(this.queryDelay || 10,
17388 this.mode == 'local' ? 10 : 250);
17391 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17393 if(this.typeAhead){
17394 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17397 if(this.editable !== false){
17398 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17401 this.indicator = this.indicatorEl();
17403 if(this.indicator){
17404 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17405 this.indicator.hide();
17410 onDestroy : function(){
17412 this.view.setStore(null);
17413 this.view.el.removeAllListeners();
17414 this.view.el.remove();
17415 this.view.purgeListeners();
17418 this.list.dom.innerHTML = '';
17422 this.store.un('beforeload', this.onBeforeLoad, this);
17423 this.store.un('load', this.onLoad, this);
17424 this.store.un('loadexception', this.onLoadException, this);
17426 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17430 fireKey : function(e){
17431 if(e.isNavKeyPress() && !this.list.isVisible()){
17432 this.fireEvent("specialkey", this, e);
17437 onResize: function(w, h)
17441 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17443 // if(typeof w != 'number'){
17444 // // we do not handle it!?!?
17447 // var tw = this.trigger.getWidth();
17448 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17449 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17451 // this.inputEl().setWidth( this.adjustWidth('input', x));
17453 // //this.trigger.setStyle('left', x+'px');
17455 // if(this.list && this.listWidth === undefined){
17456 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17457 // this.list.setWidth(lw);
17458 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17466 * Allow or prevent the user from directly editing the field text. If false is passed,
17467 * the user will only be able to select from the items defined in the dropdown list. This method
17468 * is the runtime equivalent of setting the 'editable' config option at config time.
17469 * @param {Boolean} value True to allow the user to directly edit the field text
17471 setEditable : function(value){
17472 if(value == this.editable){
17475 this.editable = value;
17477 this.inputEl().dom.setAttribute('readOnly', true);
17478 this.inputEl().on('mousedown', this.onTriggerClick, this);
17479 this.inputEl().addClass('x-combo-noedit');
17481 this.inputEl().dom.removeAttribute('readOnly');
17482 this.inputEl().un('mousedown', this.onTriggerClick, this);
17483 this.inputEl().removeClass('x-combo-noedit');
17489 onBeforeLoad : function(combo,opts){
17490 if(!this.hasFocus){
17494 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17496 this.restrictHeight();
17497 this.selectedIndex = -1;
17501 onLoad : function(){
17503 this.hasQuery = false;
17505 if(!this.hasFocus){
17509 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17510 this.loading.hide();
17513 if(this.store.getCount() > 0){
17516 this.restrictHeight();
17517 if(this.lastQuery == this.allQuery){
17518 if(this.editable && !this.tickable){
17519 this.inputEl().dom.select();
17523 !this.selectByValue(this.value, true) &&
17526 !this.store.lastOptions ||
17527 typeof(this.store.lastOptions.add) == 'undefined' ||
17528 this.store.lastOptions.add != true
17531 this.select(0, true);
17534 if(this.autoFocus){
17537 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17538 this.taTask.delay(this.typeAheadDelay);
17542 this.onEmptyResults();
17548 onLoadException : function()
17550 this.hasQuery = false;
17552 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17553 this.loading.hide();
17556 if(this.tickable && this.editable){
17561 // only causes errors at present
17562 //Roo.log(this.store.reader.jsonData);
17563 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17565 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17571 onTypeAhead : function(){
17572 if(this.store.getCount() > 0){
17573 var r = this.store.getAt(0);
17574 var newValue = r.data[this.displayField];
17575 var len = newValue.length;
17576 var selStart = this.getRawValue().length;
17578 if(selStart != len){
17579 this.setRawValue(newValue);
17580 this.selectText(selStart, newValue.length);
17586 onSelect : function(record, index){
17588 if(this.fireEvent('beforeselect', this, record, index) !== false){
17590 this.setFromData(index > -1 ? record.data : false);
17593 this.fireEvent('select', this, record, index);
17598 * Returns the currently selected field value or empty string if no value is set.
17599 * @return {String} value The selected value
17601 getValue : function()
17603 if(Roo.isIOS && this.useNativeIOS){
17604 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17608 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17611 if(this.valueField){
17612 return typeof this.value != 'undefined' ? this.value : '';
17614 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17618 getRawValue : function()
17620 if(Roo.isIOS && this.useNativeIOS){
17621 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17624 var v = this.inputEl().getValue();
17630 * Clears any text/value currently set in the field
17632 clearValue : function(){
17634 if(this.hiddenField){
17635 this.hiddenField.dom.value = '';
17638 this.setRawValue('');
17639 this.lastSelectionText = '';
17640 this.lastData = false;
17642 var close = this.closeTriggerEl();
17653 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17654 * will be displayed in the field. If the value does not match the data value of an existing item,
17655 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17656 * Otherwise the field will be blank (although the value will still be set).
17657 * @param {String} value The value to match
17659 setValue : function(v)
17661 if(Roo.isIOS && this.useNativeIOS){
17662 this.setIOSValue(v);
17672 if(this.valueField){
17673 var r = this.findRecord(this.valueField, v);
17675 text = r.data[this.displayField];
17676 }else if(this.valueNotFoundText !== undefined){
17677 text = this.valueNotFoundText;
17680 this.lastSelectionText = text;
17681 if(this.hiddenField){
17682 this.hiddenField.dom.value = v;
17684 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17687 var close = this.closeTriggerEl();
17690 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17696 * @property {Object} the last set data for the element
17701 * Sets the value of the field based on a object which is related to the record format for the store.
17702 * @param {Object} value the value to set as. or false on reset?
17704 setFromData : function(o){
17711 var dv = ''; // display value
17712 var vv = ''; // value value..
17714 if (this.displayField) {
17715 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17717 // this is an error condition!!!
17718 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17721 if(this.valueField){
17722 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17725 var close = this.closeTriggerEl();
17728 if(dv.length || vv * 1 > 0){
17730 this.blockFocus=true;
17736 if(this.hiddenField){
17737 this.hiddenField.dom.value = vv;
17739 this.lastSelectionText = dv;
17740 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17744 // no hidden field.. - we store the value in 'value', but still display
17745 // display field!!!!
17746 this.lastSelectionText = dv;
17747 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17754 reset : function(){
17755 // overridden so that last data is reset..
17762 this.setValue(this.originalValue);
17763 //this.clearInvalid();
17764 this.lastData = false;
17766 this.view.clearSelections();
17772 findRecord : function(prop, value){
17774 if(this.store.getCount() > 0){
17775 this.store.each(function(r){
17776 if(r.data[prop] == value){
17786 getName: function()
17788 // returns hidden if it's set..
17789 if (!this.rendered) {return ''};
17790 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17794 onViewMove : function(e, t){
17795 this.inKeyMode = false;
17799 onViewOver : function(e, t){
17800 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17803 var item = this.view.findItemFromChild(t);
17806 var index = this.view.indexOf(item);
17807 this.select(index, false);
17812 onViewClick : function(view, doFocus, el, e)
17814 var index = this.view.getSelectedIndexes()[0];
17816 var r = this.store.getAt(index);
17820 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17827 Roo.each(this.tickItems, function(v,k){
17829 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17831 _this.tickItems.splice(k, 1);
17833 if(typeof(e) == 'undefined' && view == false){
17834 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17846 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17847 this.tickItems.push(r.data);
17850 if(typeof(e) == 'undefined' && view == false){
17851 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17858 this.onSelect(r, index);
17860 if(doFocus !== false && !this.blockFocus){
17861 this.inputEl().focus();
17866 restrictHeight : function(){
17867 //this.innerList.dom.style.height = '';
17868 //var inner = this.innerList.dom;
17869 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17870 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17871 //this.list.beginUpdate();
17872 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17873 this.list.alignTo(this.inputEl(), this.listAlign);
17874 this.list.alignTo(this.inputEl(), this.listAlign);
17875 //this.list.endUpdate();
17879 onEmptyResults : function(){
17881 if(this.tickable && this.editable){
17882 this.hasFocus = false;
17883 this.restrictHeight();
17891 * Returns true if the dropdown list is expanded, else false.
17893 isExpanded : function(){
17894 return this.list.isVisible();
17898 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17899 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17900 * @param {String} value The data value of the item to select
17901 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17902 * selected item if it is not currently in view (defaults to true)
17903 * @return {Boolean} True if the value matched an item in the list, else false
17905 selectByValue : function(v, scrollIntoView){
17906 if(v !== undefined && v !== null){
17907 var r = this.findRecord(this.valueField || this.displayField, v);
17909 this.select(this.store.indexOf(r), scrollIntoView);
17917 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17918 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17919 * @param {Number} index The zero-based index of the list item to select
17920 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17921 * selected item if it is not currently in view (defaults to true)
17923 select : function(index, scrollIntoView){
17924 this.selectedIndex = index;
17925 this.view.select(index);
17926 if(scrollIntoView !== false){
17927 var el = this.view.getNode(index);
17929 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17932 this.list.scrollChildIntoView(el, false);
17938 selectNext : function(){
17939 var ct = this.store.getCount();
17941 if(this.selectedIndex == -1){
17943 }else if(this.selectedIndex < ct-1){
17944 this.select(this.selectedIndex+1);
17950 selectPrev : function(){
17951 var ct = this.store.getCount();
17953 if(this.selectedIndex == -1){
17955 }else if(this.selectedIndex != 0){
17956 this.select(this.selectedIndex-1);
17962 onKeyUp : function(e){
17963 if(this.editable !== false && !e.isSpecialKey()){
17964 this.lastKey = e.getKey();
17965 this.dqTask.delay(this.queryDelay);
17970 validateBlur : function(){
17971 return !this.list || !this.list.isVisible();
17975 initQuery : function(){
17977 var v = this.getRawValue();
17979 if(this.tickable && this.editable){
17980 v = this.tickableInputEl().getValue();
17987 doForce : function(){
17988 if(this.inputEl().dom.value.length > 0){
17989 this.inputEl().dom.value =
17990 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17996 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17997 * query allowing the query action to be canceled if needed.
17998 * @param {String} query The SQL query to execute
17999 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18000 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18001 * saved in the current store (defaults to false)
18003 doQuery : function(q, forceAll){
18005 if(q === undefined || q === null){
18010 forceAll: forceAll,
18014 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18019 forceAll = qe.forceAll;
18020 if(forceAll === true || (q.length >= this.minChars)){
18022 this.hasQuery = true;
18024 if(this.lastQuery != q || this.alwaysQuery){
18025 this.lastQuery = q;
18026 if(this.mode == 'local'){
18027 this.selectedIndex = -1;
18029 this.store.clearFilter();
18032 if(this.specialFilter){
18033 this.fireEvent('specialfilter', this);
18038 this.store.filter(this.displayField, q);
18041 this.store.fireEvent("datachanged", this.store);
18048 this.store.baseParams[this.queryParam] = q;
18050 var options = {params : this.getParams(q)};
18053 options.add = true;
18054 options.params.start = this.page * this.pageSize;
18057 this.store.load(options);
18060 * this code will make the page width larger, at the beginning, the list not align correctly,
18061 * we should expand the list on onLoad
18062 * so command out it
18067 this.selectedIndex = -1;
18072 this.loadNext = false;
18076 getParams : function(q){
18078 //p[this.queryParam] = q;
18082 p.limit = this.pageSize;
18088 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18090 collapse : function(){
18091 if(!this.isExpanded()){
18097 this.hasFocus = false;
18101 this.cancelBtn.hide();
18102 this.trigger.show();
18105 this.tickableInputEl().dom.value = '';
18106 this.tickableInputEl().blur();
18111 Roo.get(document).un('mousedown', this.collapseIf, this);
18112 Roo.get(document).un('mousewheel', this.collapseIf, this);
18113 if (!this.editable) {
18114 Roo.get(document).un('keydown', this.listKeyPress, this);
18116 this.fireEvent('collapse', this);
18122 collapseIf : function(e){
18123 var in_combo = e.within(this.el);
18124 var in_list = e.within(this.list);
18125 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18127 if (in_combo || in_list || is_list) {
18128 //e.stopPropagation();
18133 this.onTickableFooterButtonClick(e, false, false);
18141 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18143 expand : function(){
18145 if(this.isExpanded() || !this.hasFocus){
18149 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18150 this.list.setWidth(lw);
18156 this.restrictHeight();
18160 this.tickItems = Roo.apply([], this.item);
18163 this.cancelBtn.show();
18164 this.trigger.hide();
18167 this.tickableInputEl().focus();
18172 Roo.get(document).on('mousedown', this.collapseIf, this);
18173 Roo.get(document).on('mousewheel', this.collapseIf, this);
18174 if (!this.editable) {
18175 Roo.get(document).on('keydown', this.listKeyPress, this);
18178 this.fireEvent('expand', this);
18182 // Implements the default empty TriggerField.onTriggerClick function
18183 onTriggerClick : function(e)
18185 Roo.log('trigger click');
18187 if(this.disabled || !this.triggerList){
18192 this.loadNext = false;
18194 if(this.isExpanded()){
18196 if (!this.blockFocus) {
18197 this.inputEl().focus();
18201 this.hasFocus = true;
18202 if(this.triggerAction == 'all') {
18203 this.doQuery(this.allQuery, true);
18205 this.doQuery(this.getRawValue());
18207 if (!this.blockFocus) {
18208 this.inputEl().focus();
18213 onTickableTriggerClick : function(e)
18220 this.loadNext = false;
18221 this.hasFocus = true;
18223 if(this.triggerAction == 'all') {
18224 this.doQuery(this.allQuery, true);
18226 this.doQuery(this.getRawValue());
18230 onSearchFieldClick : function(e)
18232 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18233 this.onTickableFooterButtonClick(e, false, false);
18237 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18242 this.loadNext = false;
18243 this.hasFocus = true;
18245 if(this.triggerAction == 'all') {
18246 this.doQuery(this.allQuery, true);
18248 this.doQuery(this.getRawValue());
18252 listKeyPress : function(e)
18254 //Roo.log('listkeypress');
18255 // scroll to first matching element based on key pres..
18256 if (e.isSpecialKey()) {
18259 var k = String.fromCharCode(e.getKey()).toUpperCase();
18262 var csel = this.view.getSelectedNodes();
18263 var cselitem = false;
18265 var ix = this.view.indexOf(csel[0]);
18266 cselitem = this.store.getAt(ix);
18267 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18273 this.store.each(function(v) {
18275 // start at existing selection.
18276 if (cselitem.id == v.id) {
18282 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18283 match = this.store.indexOf(v);
18289 if (match === false) {
18290 return true; // no more action?
18293 this.view.select(match);
18294 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18295 sn.scrollIntoView(sn.dom.parentNode, false);
18298 onViewScroll : function(e, t){
18300 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){
18304 this.hasQuery = true;
18306 this.loading = this.list.select('.loading', true).first();
18308 if(this.loading === null){
18309 this.list.createChild({
18311 cls: 'loading roo-select2-more-results roo-select2-active',
18312 html: 'Loading more results...'
18315 this.loading = this.list.select('.loading', true).first();
18317 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18319 this.loading.hide();
18322 this.loading.show();
18327 this.loadNext = true;
18329 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18334 addItem : function(o)
18336 var dv = ''; // display value
18338 if (this.displayField) {
18339 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18341 // this is an error condition!!!
18342 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18349 var choice = this.choices.createChild({
18351 cls: 'roo-select2-search-choice',
18360 cls: 'roo-select2-search-choice-close fa fa-times',
18365 }, this.searchField);
18367 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18369 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18377 this.inputEl().dom.value = '';
18382 onRemoveItem : function(e, _self, o)
18384 e.preventDefault();
18386 this.lastItem = Roo.apply([], this.item);
18388 var index = this.item.indexOf(o.data) * 1;
18391 Roo.log('not this item?!');
18395 this.item.splice(index, 1);
18400 this.fireEvent('remove', this, e);
18406 syncValue : function()
18408 if(!this.item.length){
18415 Roo.each(this.item, function(i){
18416 if(_this.valueField){
18417 value.push(i[_this.valueField]);
18424 this.value = value.join(',');
18426 if(this.hiddenField){
18427 this.hiddenField.dom.value = this.value;
18430 this.store.fireEvent("datachanged", this.store);
18435 clearItem : function()
18437 if(!this.multiple){
18443 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18451 if(this.tickable && !Roo.isTouch){
18452 this.view.refresh();
18456 inputEl: function ()
18458 if(Roo.isIOS && this.useNativeIOS){
18459 return this.el.select('select.roo-ios-select', true).first();
18462 if(Roo.isTouch && this.mobileTouchView){
18463 return this.el.select('input.form-control',true).first();
18467 return this.searchField;
18470 return this.el.select('input.form-control',true).first();
18473 onTickableFooterButtonClick : function(e, btn, el)
18475 e.preventDefault();
18477 this.lastItem = Roo.apply([], this.item);
18479 if(btn && btn.name == 'cancel'){
18480 this.tickItems = Roo.apply([], this.item);
18489 Roo.each(this.tickItems, function(o){
18497 validate : function()
18499 if(this.getVisibilityEl().hasClass('hidden')){
18503 var v = this.getRawValue();
18506 v = this.getValue();
18509 if(this.disabled || this.allowBlank || v.length){
18514 this.markInvalid();
18518 tickableInputEl : function()
18520 if(!this.tickable || !this.editable){
18521 return this.inputEl();
18524 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18528 getAutoCreateTouchView : function()
18533 cls: 'form-group' //input-group
18539 type : this.inputType,
18540 cls : 'form-control x-combo-noedit',
18541 autocomplete: 'new-password',
18542 placeholder : this.placeholder || '',
18547 input.name = this.name;
18551 input.cls += ' input-' + this.size;
18554 if (this.disabled) {
18555 input.disabled = true;
18559 cls : 'roo-combobox-wrap',
18566 inputblock.cls += ' input-group';
18568 inputblock.cn.unshift({
18570 cls : 'input-group-addon input-group-prepend input-group-text',
18575 if(this.removable && !this.multiple){
18576 inputblock.cls += ' roo-removable';
18578 inputblock.cn.push({
18581 cls : 'roo-combo-removable-btn close'
18585 if(this.hasFeedback && !this.allowBlank){
18587 inputblock.cls += ' has-feedback';
18589 inputblock.cn.push({
18591 cls: 'glyphicon form-control-feedback'
18598 inputblock.cls += (this.before) ? '' : ' input-group';
18600 inputblock.cn.push({
18602 cls : 'input-group-addon input-group-append input-group-text',
18608 var ibwrap = inputblock;
18613 cls: 'roo-select2-choices',
18617 cls: 'roo-select2-search-field',
18630 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18635 cls: 'form-hidden-field'
18641 if(!this.multiple && this.showToggleBtn){
18647 if (this.caret != false) {
18650 cls: 'fa fa-' + this.caret
18657 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18659 Roo.bootstrap.version == 3 ? caret : '',
18662 cls: 'combobox-clear',
18676 combobox.cls += ' roo-select2-container-multi';
18679 var required = this.allowBlank ? {
18681 style: 'display: none'
18684 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18685 tooltip : 'This field is required'
18688 var align = this.labelAlign || this.parentLabelAlign();
18690 if (align ==='left' && this.fieldLabel.length) {
18696 cls : 'control-label col-form-label',
18697 html : this.fieldLabel
18701 cls : 'roo-combobox-wrap ',
18708 var labelCfg = cfg.cn[1];
18709 var contentCfg = cfg.cn[2];
18712 if(this.indicatorpos == 'right'){
18717 cls : 'control-label col-form-label',
18721 html : this.fieldLabel
18727 cls : "roo-combobox-wrap ",
18735 labelCfg = cfg.cn[0];
18736 contentCfg = cfg.cn[1];
18741 if(this.labelWidth > 12){
18742 labelCfg.style = "width: " + this.labelWidth + 'px';
18745 if(this.labelWidth < 13 && this.labelmd == 0){
18746 this.labelmd = this.labelWidth;
18749 if(this.labellg > 0){
18750 labelCfg.cls += ' col-lg-' + this.labellg;
18751 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18754 if(this.labelmd > 0){
18755 labelCfg.cls += ' col-md-' + this.labelmd;
18756 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18759 if(this.labelsm > 0){
18760 labelCfg.cls += ' col-sm-' + this.labelsm;
18761 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18764 if(this.labelxs > 0){
18765 labelCfg.cls += ' col-xs-' + this.labelxs;
18766 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18770 } else if ( this.fieldLabel.length) {
18775 cls : 'control-label',
18776 html : this.fieldLabel
18787 if(this.indicatorpos == 'right'){
18791 cls : 'control-label',
18792 html : this.fieldLabel,
18810 var settings = this;
18812 ['xs','sm','md','lg'].map(function(size){
18813 if (settings[size]) {
18814 cfg.cls += ' col-' + size + '-' + settings[size];
18821 initTouchView : function()
18823 this.renderTouchView();
18825 this.touchViewEl.on('scroll', function(){
18826 this.el.dom.scrollTop = 0;
18829 this.originalValue = this.getValue();
18831 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18833 this.inputEl().on("click", this.showTouchView, this);
18834 if (this.triggerEl) {
18835 this.triggerEl.on("click", this.showTouchView, this);
18839 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18840 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18842 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18844 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18845 this.store.on('load', this.onTouchViewLoad, this);
18846 this.store.on('loadexception', this.onTouchViewLoadException, this);
18848 if(this.hiddenName){
18850 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18852 this.hiddenField.dom.value =
18853 this.hiddenValue !== undefined ? this.hiddenValue :
18854 this.value !== undefined ? this.value : '';
18856 this.el.dom.removeAttribute('name');
18857 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18861 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18862 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18865 if(this.removable && !this.multiple){
18866 var close = this.closeTriggerEl();
18868 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18869 close.on('click', this.removeBtnClick, this, close);
18873 * fix the bug in Safari iOS8
18875 this.inputEl().on("focus", function(e){
18876 document.activeElement.blur();
18879 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18886 renderTouchView : function()
18888 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18889 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18892 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18894 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18895 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18896 this.touchViewBodyEl.setStyle('overflow', 'auto');
18898 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18899 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18901 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18902 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18906 showTouchView : function()
18912 this.touchViewHeaderEl.hide();
18914 if(this.modalTitle.length){
18915 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18916 this.touchViewHeaderEl.show();
18919 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18920 this.touchViewEl.show();
18922 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18924 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18925 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18927 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18929 if(this.modalTitle.length){
18930 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18933 this.touchViewBodyEl.setHeight(bodyHeight);
18937 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18939 this.touchViewEl.addClass(['in','show']);
18942 if(this._touchViewMask){
18943 Roo.get(document.body).addClass("x-body-masked");
18944 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18945 this._touchViewMask.setStyle('z-index', 10000);
18946 this._touchViewMask.addClass('show');
18949 this.doTouchViewQuery();
18953 hideTouchView : function()
18955 this.touchViewEl.removeClass(['in','show']);
18959 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18961 this.touchViewEl.setStyle('display', 'none');
18964 if(this._touchViewMask){
18965 this._touchViewMask.removeClass('show');
18966 Roo.get(document.body).removeClass("x-body-masked");
18970 setTouchViewValue : function()
18977 Roo.each(this.tickItems, function(o){
18982 this.hideTouchView();
18985 doTouchViewQuery : function()
18994 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18998 if(!this.alwaysQuery || this.mode == 'local'){
18999 this.onTouchViewLoad();
19006 onTouchViewBeforeLoad : function(combo,opts)
19012 onTouchViewLoad : function()
19014 if(this.store.getCount() < 1){
19015 this.onTouchViewEmptyResults();
19019 this.clearTouchView();
19021 var rawValue = this.getRawValue();
19023 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19025 this.tickItems = [];
19027 this.store.data.each(function(d, rowIndex){
19028 var row = this.touchViewListGroup.createChild(template);
19030 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19031 row.addClass(d.data.cls);
19034 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19037 html : d.data[this.displayField]
19040 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19041 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19044 row.removeClass('selected');
19045 if(!this.multiple && this.valueField &&
19046 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19049 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19050 row.addClass('selected');
19053 if(this.multiple && this.valueField &&
19054 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19058 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19059 this.tickItems.push(d.data);
19062 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19066 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19068 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19070 if(this.modalTitle.length){
19071 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19074 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19076 if(this.mobile_restrict_height && listHeight < bodyHeight){
19077 this.touchViewBodyEl.setHeight(listHeight);
19082 if(firstChecked && listHeight > bodyHeight){
19083 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19088 onTouchViewLoadException : function()
19090 this.hideTouchView();
19093 onTouchViewEmptyResults : function()
19095 this.clearTouchView();
19097 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19099 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19103 clearTouchView : function()
19105 this.touchViewListGroup.dom.innerHTML = '';
19108 onTouchViewClick : function(e, el, o)
19110 e.preventDefault();
19113 var rowIndex = o.rowIndex;
19115 var r = this.store.getAt(rowIndex);
19117 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19119 if(!this.multiple){
19120 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19121 c.dom.removeAttribute('checked');
19124 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19126 this.setFromData(r.data);
19128 var close = this.closeTriggerEl();
19134 this.hideTouchView();
19136 this.fireEvent('select', this, r, rowIndex);
19141 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19142 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19143 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19147 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19148 this.addItem(r.data);
19149 this.tickItems.push(r.data);
19153 getAutoCreateNativeIOS : function()
19156 cls: 'form-group' //input-group,
19161 cls : 'roo-ios-select'
19165 combobox.name = this.name;
19168 if (this.disabled) {
19169 combobox.disabled = true;
19172 var settings = this;
19174 ['xs','sm','md','lg'].map(function(size){
19175 if (settings[size]) {
19176 cfg.cls += ' col-' + size + '-' + settings[size];
19186 initIOSView : function()
19188 this.store.on('load', this.onIOSViewLoad, this);
19193 onIOSViewLoad : function()
19195 if(this.store.getCount() < 1){
19199 this.clearIOSView();
19201 if(this.allowBlank) {
19203 var default_text = '-- SELECT --';
19205 if(this.placeholder.length){
19206 default_text = this.placeholder;
19209 if(this.emptyTitle.length){
19210 default_text += ' - ' + this.emptyTitle + ' -';
19213 var opt = this.inputEl().createChild({
19216 html : default_text
19220 o[this.valueField] = 0;
19221 o[this.displayField] = default_text;
19223 this.ios_options.push({
19230 this.store.data.each(function(d, rowIndex){
19234 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19235 html = d.data[this.displayField];
19240 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19241 value = d.data[this.valueField];
19250 if(this.value == d.data[this.valueField]){
19251 option['selected'] = true;
19254 var opt = this.inputEl().createChild(option);
19256 this.ios_options.push({
19263 this.inputEl().on('change', function(){
19264 this.fireEvent('select', this);
19269 clearIOSView: function()
19271 this.inputEl().dom.innerHTML = '';
19273 this.ios_options = [];
19276 setIOSValue: function(v)
19280 if(!this.ios_options){
19284 Roo.each(this.ios_options, function(opts){
19286 opts.el.dom.removeAttribute('selected');
19288 if(opts.data[this.valueField] != v){
19292 opts.el.dom.setAttribute('selected', true);
19298 * @cfg {Boolean} grow
19302 * @cfg {Number} growMin
19306 * @cfg {Number} growMax
19315 Roo.apply(Roo.bootstrap.ComboBox, {
19319 cls: 'modal-header',
19341 cls: 'list-group-item',
19345 cls: 'roo-combobox-list-group-item-value'
19349 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19363 listItemCheckbox : {
19365 cls: 'list-group-item',
19369 cls: 'roo-combobox-list-group-item-value'
19373 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19389 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19394 cls: 'modal-footer',
19402 cls: 'col-xs-6 text-left',
19405 cls: 'btn btn-danger roo-touch-view-cancel',
19411 cls: 'col-xs-6 text-right',
19414 cls: 'btn btn-success roo-touch-view-ok',
19425 Roo.apply(Roo.bootstrap.ComboBox, {
19427 touchViewTemplate : {
19429 cls: 'modal fade roo-combobox-touch-view',
19433 cls: 'modal-dialog',
19434 style : 'position:fixed', // we have to fix position....
19438 cls: 'modal-content',
19440 Roo.bootstrap.ComboBox.header,
19441 Roo.bootstrap.ComboBox.body,
19442 Roo.bootstrap.ComboBox.footer
19451 * Ext JS Library 1.1.1
19452 * Copyright(c) 2006-2007, Ext JS, LLC.
19454 * Originally Released Under LGPL - original licence link has changed is not relivant.
19457 * <script type="text/javascript">
19462 * @extends Roo.util.Observable
19463 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19464 * This class also supports single and multi selection modes. <br>
19465 * Create a data model bound view:
19467 var store = new Roo.data.Store(...);
19469 var view = new Roo.View({
19471 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19473 singleSelect: true,
19474 selectedClass: "ydataview-selected",
19478 // listen for node click?
19479 view.on("click", function(vw, index, node, e){
19480 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19484 dataModel.load("foobar.xml");
19486 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19488 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19489 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19491 * Note: old style constructor is still suported (container, template, config)
19494 * Create a new View
19495 * @param {Object} config The config object
19498 Roo.View = function(config, depreciated_tpl, depreciated_config){
19500 this.parent = false;
19502 if (typeof(depreciated_tpl) == 'undefined') {
19503 // new way.. - universal constructor.
19504 Roo.apply(this, config);
19505 this.el = Roo.get(this.el);
19508 this.el = Roo.get(config);
19509 this.tpl = depreciated_tpl;
19510 Roo.apply(this, depreciated_config);
19512 this.wrapEl = this.el.wrap().wrap();
19513 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19516 if(typeof(this.tpl) == "string"){
19517 this.tpl = new Roo.Template(this.tpl);
19519 // support xtype ctors..
19520 this.tpl = new Roo.factory(this.tpl, Roo);
19524 this.tpl.compile();
19529 * @event beforeclick
19530 * Fires before a click is processed. Returns false to cancel the default action.
19531 * @param {Roo.View} this
19532 * @param {Number} index The index of the target node
19533 * @param {HTMLElement} node The target node
19534 * @param {Roo.EventObject} e The raw event object
19536 "beforeclick" : true,
19539 * Fires when a template node is clicked.
19540 * @param {Roo.View} this
19541 * @param {Number} index The index of the target node
19542 * @param {HTMLElement} node The target node
19543 * @param {Roo.EventObject} e The raw event object
19548 * Fires when a template node is double clicked.
19549 * @param {Roo.View} this
19550 * @param {Number} index The index of the target node
19551 * @param {HTMLElement} node The target node
19552 * @param {Roo.EventObject} e The raw event object
19556 * @event contextmenu
19557 * Fires when a template node is right clicked.
19558 * @param {Roo.View} this
19559 * @param {Number} index The index of the target node
19560 * @param {HTMLElement} node The target node
19561 * @param {Roo.EventObject} e The raw event object
19563 "contextmenu" : true,
19565 * @event selectionchange
19566 * Fires when the selected nodes change.
19567 * @param {Roo.View} this
19568 * @param {Array} selections Array of the selected nodes
19570 "selectionchange" : true,
19573 * @event beforeselect
19574 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19575 * @param {Roo.View} this
19576 * @param {HTMLElement} node The node to be selected
19577 * @param {Array} selections Array of currently selected nodes
19579 "beforeselect" : true,
19581 * @event preparedata
19582 * Fires on every row to render, to allow you to change the data.
19583 * @param {Roo.View} this
19584 * @param {Object} data to be rendered (change this)
19586 "preparedata" : true
19594 "click": this.onClick,
19595 "dblclick": this.onDblClick,
19596 "contextmenu": this.onContextMenu,
19600 this.selections = [];
19602 this.cmp = new Roo.CompositeElementLite([]);
19604 this.store = Roo.factory(this.store, Roo.data);
19605 this.setStore(this.store, true);
19608 if ( this.footer && this.footer.xtype) {
19610 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19612 this.footer.dataSource = this.store;
19613 this.footer.container = fctr;
19614 this.footer = Roo.factory(this.footer, Roo);
19615 fctr.insertFirst(this.el);
19617 // this is a bit insane - as the paging toolbar seems to detach the el..
19618 // dom.parentNode.parentNode.parentNode
19619 // they get detached?
19623 Roo.View.superclass.constructor.call(this);
19628 Roo.extend(Roo.View, Roo.util.Observable, {
19631 * @cfg {Roo.data.Store} store Data store to load data from.
19636 * @cfg {String|Roo.Element} el The container element.
19641 * @cfg {String|Roo.Template} tpl The template used by this View
19645 * @cfg {String} dataName the named area of the template to use as the data area
19646 * Works with domtemplates roo-name="name"
19650 * @cfg {String} selectedClass The css class to add to selected nodes
19652 selectedClass : "x-view-selected",
19654 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19659 * @cfg {String} text to display on mask (default Loading)
19663 * @cfg {Boolean} multiSelect Allow multiple selection
19665 multiSelect : false,
19667 * @cfg {Boolean} singleSelect Allow single selection
19669 singleSelect: false,
19672 * @cfg {Boolean} toggleSelect - selecting
19674 toggleSelect : false,
19677 * @cfg {Boolean} tickable - selecting
19682 * Returns the element this view is bound to.
19683 * @return {Roo.Element}
19685 getEl : function(){
19686 return this.wrapEl;
19692 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19694 refresh : function(){
19695 //Roo.log('refresh');
19698 // if we are using something like 'domtemplate', then
19699 // the what gets used is:
19700 // t.applySubtemplate(NAME, data, wrapping data..)
19701 // the outer template then get' applied with
19702 // the store 'extra data'
19703 // and the body get's added to the
19704 // roo-name="data" node?
19705 // <span class='roo-tpl-{name}'></span> ?????
19709 this.clearSelections();
19710 this.el.update("");
19712 var records = this.store.getRange();
19713 if(records.length < 1) {
19715 // is this valid?? = should it render a template??
19717 this.el.update(this.emptyText);
19721 if (this.dataName) {
19722 this.el.update(t.apply(this.store.meta)); //????
19723 el = this.el.child('.roo-tpl-' + this.dataName);
19726 for(var i = 0, len = records.length; i < len; i++){
19727 var data = this.prepareData(records[i].data, i, records[i]);
19728 this.fireEvent("preparedata", this, data, i, records[i]);
19730 var d = Roo.apply({}, data);
19733 Roo.apply(d, {'roo-id' : Roo.id()});
19737 Roo.each(this.parent.item, function(item){
19738 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19741 Roo.apply(d, {'roo-data-checked' : 'checked'});
19745 html[html.length] = Roo.util.Format.trim(
19747 t.applySubtemplate(this.dataName, d, this.store.meta) :
19754 el.update(html.join(""));
19755 this.nodes = el.dom.childNodes;
19756 this.updateIndexes(0);
19761 * Function to override to reformat the data that is sent to
19762 * the template for each node.
19763 * DEPRICATED - use the preparedata event handler.
19764 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19765 * a JSON object for an UpdateManager bound view).
19767 prepareData : function(data, index, record)
19769 this.fireEvent("preparedata", this, data, index, record);
19773 onUpdate : function(ds, record){
19774 // Roo.log('on update');
19775 this.clearSelections();
19776 var index = this.store.indexOf(record);
19777 var n = this.nodes[index];
19778 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19779 n.parentNode.removeChild(n);
19780 this.updateIndexes(index, index);
19786 onAdd : function(ds, records, index)
19788 //Roo.log(['on Add', ds, records, index] );
19789 this.clearSelections();
19790 if(this.nodes.length == 0){
19794 var n = this.nodes[index];
19795 for(var i = 0, len = records.length; i < len; i++){
19796 var d = this.prepareData(records[i].data, i, records[i]);
19798 this.tpl.insertBefore(n, d);
19801 this.tpl.append(this.el, d);
19804 this.updateIndexes(index);
19807 onRemove : function(ds, record, index){
19808 // Roo.log('onRemove');
19809 this.clearSelections();
19810 var el = this.dataName ?
19811 this.el.child('.roo-tpl-' + this.dataName) :
19814 el.dom.removeChild(this.nodes[index]);
19815 this.updateIndexes(index);
19819 * Refresh an individual node.
19820 * @param {Number} index
19822 refreshNode : function(index){
19823 this.onUpdate(this.store, this.store.getAt(index));
19826 updateIndexes : function(startIndex, endIndex){
19827 var ns = this.nodes;
19828 startIndex = startIndex || 0;
19829 endIndex = endIndex || ns.length - 1;
19830 for(var i = startIndex; i <= endIndex; i++){
19831 ns[i].nodeIndex = i;
19836 * Changes the data store this view uses and refresh the view.
19837 * @param {Store} store
19839 setStore : function(store, initial){
19840 if(!initial && this.store){
19841 this.store.un("datachanged", this.refresh);
19842 this.store.un("add", this.onAdd);
19843 this.store.un("remove", this.onRemove);
19844 this.store.un("update", this.onUpdate);
19845 this.store.un("clear", this.refresh);
19846 this.store.un("beforeload", this.onBeforeLoad);
19847 this.store.un("load", this.onLoad);
19848 this.store.un("loadexception", this.onLoad);
19852 store.on("datachanged", this.refresh, this);
19853 store.on("add", this.onAdd, this);
19854 store.on("remove", this.onRemove, this);
19855 store.on("update", this.onUpdate, this);
19856 store.on("clear", this.refresh, this);
19857 store.on("beforeload", this.onBeforeLoad, this);
19858 store.on("load", this.onLoad, this);
19859 store.on("loadexception", this.onLoad, this);
19867 * onbeforeLoad - masks the loading area.
19870 onBeforeLoad : function(store,opts)
19872 //Roo.log('onBeforeLoad');
19874 this.el.update("");
19876 this.el.mask(this.mask ? this.mask : "Loading" );
19878 onLoad : function ()
19885 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19886 * @param {HTMLElement} node
19887 * @return {HTMLElement} The template node
19889 findItemFromChild : function(node){
19890 var el = this.dataName ?
19891 this.el.child('.roo-tpl-' + this.dataName,true) :
19894 if(!node || node.parentNode == el){
19897 var p = node.parentNode;
19898 while(p && p != el){
19899 if(p.parentNode == el){
19908 onClick : function(e){
19909 var item = this.findItemFromChild(e.getTarget());
19911 var index = this.indexOf(item);
19912 if(this.onItemClick(item, index, e) !== false){
19913 this.fireEvent("click", this, index, item, e);
19916 this.clearSelections();
19921 onContextMenu : function(e){
19922 var item = this.findItemFromChild(e.getTarget());
19924 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19929 onDblClick : function(e){
19930 var item = this.findItemFromChild(e.getTarget());
19932 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19936 onItemClick : function(item, index, e)
19938 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19941 if (this.toggleSelect) {
19942 var m = this.isSelected(item) ? 'unselect' : 'select';
19945 _t[m](item, true, false);
19948 if(this.multiSelect || this.singleSelect){
19949 if(this.multiSelect && e.shiftKey && this.lastSelection){
19950 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19952 this.select(item, this.multiSelect && e.ctrlKey);
19953 this.lastSelection = item;
19956 if(!this.tickable){
19957 e.preventDefault();
19965 * Get the number of selected nodes.
19968 getSelectionCount : function(){
19969 return this.selections.length;
19973 * Get the currently selected nodes.
19974 * @return {Array} An array of HTMLElements
19976 getSelectedNodes : function(){
19977 return this.selections;
19981 * Get the indexes of the selected nodes.
19984 getSelectedIndexes : function(){
19985 var indexes = [], s = this.selections;
19986 for(var i = 0, len = s.length; i < len; i++){
19987 indexes.push(s[i].nodeIndex);
19993 * Clear all selections
19994 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19996 clearSelections : function(suppressEvent){
19997 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19998 this.cmp.elements = this.selections;
19999 this.cmp.removeClass(this.selectedClass);
20000 this.selections = [];
20001 if(!suppressEvent){
20002 this.fireEvent("selectionchange", this, this.selections);
20008 * Returns true if the passed node is selected
20009 * @param {HTMLElement/Number} node The node or node index
20010 * @return {Boolean}
20012 isSelected : function(node){
20013 var s = this.selections;
20017 node = this.getNode(node);
20018 return s.indexOf(node) !== -1;
20023 * @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
20024 * @param {Boolean} keepExisting (optional) true to keep existing selections
20025 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20027 select : function(nodeInfo, keepExisting, suppressEvent){
20028 if(nodeInfo instanceof Array){
20030 this.clearSelections(true);
20032 for(var i = 0, len = nodeInfo.length; i < len; i++){
20033 this.select(nodeInfo[i], true, true);
20037 var node = this.getNode(nodeInfo);
20038 if(!node || this.isSelected(node)){
20039 return; // already selected.
20042 this.clearSelections(true);
20045 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20046 Roo.fly(node).addClass(this.selectedClass);
20047 this.selections.push(node);
20048 if(!suppressEvent){
20049 this.fireEvent("selectionchange", this, this.selections);
20057 * @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
20058 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20059 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20061 unselect : function(nodeInfo, keepExisting, suppressEvent)
20063 if(nodeInfo instanceof Array){
20064 Roo.each(this.selections, function(s) {
20065 this.unselect(s, nodeInfo);
20069 var node = this.getNode(nodeInfo);
20070 if(!node || !this.isSelected(node)){
20071 //Roo.log("not selected");
20072 return; // not selected.
20076 Roo.each(this.selections, function(s) {
20078 Roo.fly(node).removeClass(this.selectedClass);
20085 this.selections= ns;
20086 this.fireEvent("selectionchange", this, this.selections);
20090 * Gets a template node.
20091 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20092 * @return {HTMLElement} The node or null if it wasn't found
20094 getNode : function(nodeInfo){
20095 if(typeof nodeInfo == "string"){
20096 return document.getElementById(nodeInfo);
20097 }else if(typeof nodeInfo == "number"){
20098 return this.nodes[nodeInfo];
20104 * Gets a range template nodes.
20105 * @param {Number} startIndex
20106 * @param {Number} endIndex
20107 * @return {Array} An array of nodes
20109 getNodes : function(start, end){
20110 var ns = this.nodes;
20111 start = start || 0;
20112 end = typeof end == "undefined" ? ns.length - 1 : end;
20115 for(var i = start; i <= end; i++){
20119 for(var i = start; i >= end; i--){
20127 * Finds the index of the passed node
20128 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20129 * @return {Number} The index of the node or -1
20131 indexOf : function(node){
20132 node = this.getNode(node);
20133 if(typeof node.nodeIndex == "number"){
20134 return node.nodeIndex;
20136 var ns = this.nodes;
20137 for(var i = 0, len = ns.length; i < len; i++){
20148 * based on jquery fullcalendar
20152 Roo.bootstrap = Roo.bootstrap || {};
20154 * @class Roo.bootstrap.Calendar
20155 * @extends Roo.bootstrap.Component
20156 * Bootstrap Calendar class
20157 * @cfg {Boolean} loadMask (true|false) default false
20158 * @cfg {Object} header generate the user specific header of the calendar, default false
20161 * Create a new Container
20162 * @param {Object} config The config object
20167 Roo.bootstrap.Calendar = function(config){
20168 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20172 * Fires when a date is selected
20173 * @param {DatePicker} this
20174 * @param {Date} date The selected date
20178 * @event monthchange
20179 * Fires when the displayed month changes
20180 * @param {DatePicker} this
20181 * @param {Date} date The selected month
20183 'monthchange': true,
20185 * @event evententer
20186 * Fires when mouse over an event
20187 * @param {Calendar} this
20188 * @param {event} Event
20190 'evententer': true,
20192 * @event eventleave
20193 * Fires when the mouse leaves an
20194 * @param {Calendar} this
20197 'eventleave': true,
20199 * @event eventclick
20200 * Fires when the mouse click an
20201 * @param {Calendar} this
20210 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20213 * @cfg {Roo.data.Store} store
20214 * The data source for the calendar
20218 * @cfg {Number} startDay
20219 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20227 getAutoCreate : function(){
20230 var fc_button = function(name, corner, style, content ) {
20231 return Roo.apply({},{
20233 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20235 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20238 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20249 style : 'width:100%',
20256 cls : 'fc-header-left',
20258 fc_button('prev', 'left', 'arrow', '‹' ),
20259 fc_button('next', 'right', 'arrow', '›' ),
20260 { tag: 'span', cls: 'fc-header-space' },
20261 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20269 cls : 'fc-header-center',
20273 cls: 'fc-header-title',
20276 html : 'month / year'
20284 cls : 'fc-header-right',
20286 /* fc_button('month', 'left', '', 'month' ),
20287 fc_button('week', '', '', 'week' ),
20288 fc_button('day', 'right', '', 'day' )
20300 header = this.header;
20303 var cal_heads = function() {
20305 // fixme - handle this.
20307 for (var i =0; i < Date.dayNames.length; i++) {
20308 var d = Date.dayNames[i];
20311 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20312 html : d.substring(0,3)
20316 ret[0].cls += ' fc-first';
20317 ret[6].cls += ' fc-last';
20320 var cal_cell = function(n) {
20323 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20328 cls: 'fc-day-number',
20332 cls: 'fc-day-content',
20336 style: 'position: relative;' // height: 17px;
20348 var cal_rows = function() {
20351 for (var r = 0; r < 6; r++) {
20358 for (var i =0; i < Date.dayNames.length; i++) {
20359 var d = Date.dayNames[i];
20360 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20363 row.cn[0].cls+=' fc-first';
20364 row.cn[0].cn[0].style = 'min-height:90px';
20365 row.cn[6].cls+=' fc-last';
20369 ret[0].cls += ' fc-first';
20370 ret[4].cls += ' fc-prev-last';
20371 ret[5].cls += ' fc-last';
20378 cls: 'fc-border-separate',
20379 style : 'width:100%',
20387 cls : 'fc-first fc-last',
20405 cls : 'fc-content',
20406 style : "position: relative;",
20409 cls : 'fc-view fc-view-month fc-grid',
20410 style : 'position: relative',
20411 unselectable : 'on',
20414 cls : 'fc-event-container',
20415 style : 'position:absolute;z-index:8;top:0;left:0;'
20433 initEvents : function()
20436 throw "can not find store for calendar";
20442 style: "text-align:center",
20446 style: "background-color:white;width:50%;margin:250 auto",
20450 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20461 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20463 var size = this.el.select('.fc-content', true).first().getSize();
20464 this.maskEl.setSize(size.width, size.height);
20465 this.maskEl.enableDisplayMode("block");
20466 if(!this.loadMask){
20467 this.maskEl.hide();
20470 this.store = Roo.factory(this.store, Roo.data);
20471 this.store.on('load', this.onLoad, this);
20472 this.store.on('beforeload', this.onBeforeLoad, this);
20476 this.cells = this.el.select('.fc-day',true);
20477 //Roo.log(this.cells);
20478 this.textNodes = this.el.query('.fc-day-number');
20479 this.cells.addClassOnOver('fc-state-hover');
20481 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20482 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20483 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20484 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20486 this.on('monthchange', this.onMonthChange, this);
20488 this.update(new Date().clearTime());
20491 resize : function() {
20492 var sz = this.el.getSize();
20494 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20495 this.el.select('.fc-day-content div',true).setHeight(34);
20500 showPrevMonth : function(e){
20501 this.update(this.activeDate.add("mo", -1));
20503 showToday : function(e){
20504 this.update(new Date().clearTime());
20507 showNextMonth : function(e){
20508 this.update(this.activeDate.add("mo", 1));
20512 showPrevYear : function(){
20513 this.update(this.activeDate.add("y", -1));
20517 showNextYear : function(){
20518 this.update(this.activeDate.add("y", 1));
20523 update : function(date)
20525 var vd = this.activeDate;
20526 this.activeDate = date;
20527 // if(vd && this.el){
20528 // var t = date.getTime();
20529 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20530 // Roo.log('using add remove');
20532 // this.fireEvent('monthchange', this, date);
20534 // this.cells.removeClass("fc-state-highlight");
20535 // this.cells.each(function(c){
20536 // if(c.dateValue == t){
20537 // c.addClass("fc-state-highlight");
20538 // setTimeout(function(){
20539 // try{c.dom.firstChild.focus();}catch(e){}
20549 var days = date.getDaysInMonth();
20551 var firstOfMonth = date.getFirstDateOfMonth();
20552 var startingPos = firstOfMonth.getDay()-this.startDay;
20554 if(startingPos < this.startDay){
20558 var pm = date.add(Date.MONTH, -1);
20559 var prevStart = pm.getDaysInMonth()-startingPos;
20561 this.cells = this.el.select('.fc-day',true);
20562 this.textNodes = this.el.query('.fc-day-number');
20563 this.cells.addClassOnOver('fc-state-hover');
20565 var cells = this.cells.elements;
20566 var textEls = this.textNodes;
20568 Roo.each(cells, function(cell){
20569 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20572 days += startingPos;
20574 // convert everything to numbers so it's fast
20575 var day = 86400000;
20576 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20579 //Roo.log(prevStart);
20581 var today = new Date().clearTime().getTime();
20582 var sel = date.clearTime().getTime();
20583 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20584 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20585 var ddMatch = this.disabledDatesRE;
20586 var ddText = this.disabledDatesText;
20587 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20588 var ddaysText = this.disabledDaysText;
20589 var format = this.format;
20591 var setCellClass = function(cal, cell){
20595 //Roo.log('set Cell Class');
20597 var t = d.getTime();
20601 cell.dateValue = t;
20603 cell.className += " fc-today";
20604 cell.className += " fc-state-highlight";
20605 cell.title = cal.todayText;
20608 // disable highlight in other month..
20609 //cell.className += " fc-state-highlight";
20614 cell.className = " fc-state-disabled";
20615 cell.title = cal.minText;
20619 cell.className = " fc-state-disabled";
20620 cell.title = cal.maxText;
20624 if(ddays.indexOf(d.getDay()) != -1){
20625 cell.title = ddaysText;
20626 cell.className = " fc-state-disabled";
20629 if(ddMatch && format){
20630 var fvalue = d.dateFormat(format);
20631 if(ddMatch.test(fvalue)){
20632 cell.title = ddText.replace("%0", fvalue);
20633 cell.className = " fc-state-disabled";
20637 if (!cell.initialClassName) {
20638 cell.initialClassName = cell.dom.className;
20641 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20646 for(; i < startingPos; i++) {
20647 textEls[i].innerHTML = (++prevStart);
20648 d.setDate(d.getDate()+1);
20650 cells[i].className = "fc-past fc-other-month";
20651 setCellClass(this, cells[i]);
20656 for(; i < days; i++){
20657 intDay = i - startingPos + 1;
20658 textEls[i].innerHTML = (intDay);
20659 d.setDate(d.getDate()+1);
20661 cells[i].className = ''; // "x-date-active";
20662 setCellClass(this, cells[i]);
20666 for(; i < 42; i++) {
20667 textEls[i].innerHTML = (++extraDays);
20668 d.setDate(d.getDate()+1);
20670 cells[i].className = "fc-future fc-other-month";
20671 setCellClass(this, cells[i]);
20674 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20676 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20678 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20679 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20681 if(totalRows != 6){
20682 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20683 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20686 this.fireEvent('monthchange', this, date);
20690 if(!this.internalRender){
20691 var main = this.el.dom.firstChild;
20692 var w = main.offsetWidth;
20693 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20694 Roo.fly(main).setWidth(w);
20695 this.internalRender = true;
20696 // opera does not respect the auto grow header center column
20697 // then, after it gets a width opera refuses to recalculate
20698 // without a second pass
20699 if(Roo.isOpera && !this.secondPass){
20700 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20701 this.secondPass = true;
20702 this.update.defer(10, this, [date]);
20709 findCell : function(dt) {
20710 dt = dt.clearTime().getTime();
20712 this.cells.each(function(c){
20713 //Roo.log("check " +c.dateValue + '?=' + dt);
20714 if(c.dateValue == dt){
20724 findCells : function(ev) {
20725 var s = ev.start.clone().clearTime().getTime();
20727 var e= ev.end.clone().clearTime().getTime();
20730 this.cells.each(function(c){
20731 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20733 if(c.dateValue > e){
20736 if(c.dateValue < s){
20745 // findBestRow: function(cells)
20749 // for (var i =0 ; i < cells.length;i++) {
20750 // ret = Math.max(cells[i].rows || 0,ret);
20757 addItem : function(ev)
20759 // look for vertical location slot in
20760 var cells = this.findCells(ev);
20762 // ev.row = this.findBestRow(cells);
20764 // work out the location.
20768 for(var i =0; i < cells.length; i++) {
20770 cells[i].row = cells[0].row;
20773 cells[i].row = cells[i].row + 1;
20783 if (crow.start.getY() == cells[i].getY()) {
20785 crow.end = cells[i];
20802 cells[0].events.push(ev);
20804 this.calevents.push(ev);
20807 clearEvents: function() {
20809 if(!this.calevents){
20813 Roo.each(this.cells.elements, function(c){
20819 Roo.each(this.calevents, function(e) {
20820 Roo.each(e.els, function(el) {
20821 el.un('mouseenter' ,this.onEventEnter, this);
20822 el.un('mouseleave' ,this.onEventLeave, this);
20827 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20833 renderEvents: function()
20837 this.cells.each(function(c) {
20846 if(c.row != c.events.length){
20847 r = 4 - (4 - (c.row - c.events.length));
20850 c.events = ev.slice(0, r);
20851 c.more = ev.slice(r);
20853 if(c.more.length && c.more.length == 1){
20854 c.events.push(c.more.pop());
20857 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20861 this.cells.each(function(c) {
20863 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20866 for (var e = 0; e < c.events.length; e++){
20867 var ev = c.events[e];
20868 var rows = ev.rows;
20870 for(var i = 0; i < rows.length; i++) {
20872 // how many rows should it span..
20875 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20876 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20878 unselectable : "on",
20881 cls: 'fc-event-inner',
20885 // cls: 'fc-event-time',
20886 // html : cells.length > 1 ? '' : ev.time
20890 cls: 'fc-event-title',
20891 html : String.format('{0}', ev.title)
20898 cls: 'ui-resizable-handle ui-resizable-e',
20899 html : '  '
20906 cfg.cls += ' fc-event-start';
20908 if ((i+1) == rows.length) {
20909 cfg.cls += ' fc-event-end';
20912 var ctr = _this.el.select('.fc-event-container',true).first();
20913 var cg = ctr.createChild(cfg);
20915 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20916 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20918 var r = (c.more.length) ? 1 : 0;
20919 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20920 cg.setWidth(ebox.right - sbox.x -2);
20922 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20923 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20924 cg.on('click', _this.onEventClick, _this, ev);
20935 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20936 style : 'position: absolute',
20937 unselectable : "on",
20940 cls: 'fc-event-inner',
20944 cls: 'fc-event-title',
20952 cls: 'ui-resizable-handle ui-resizable-e',
20953 html : '  '
20959 var ctr = _this.el.select('.fc-event-container',true).first();
20960 var cg = ctr.createChild(cfg);
20962 var sbox = c.select('.fc-day-content',true).first().getBox();
20963 var ebox = c.select('.fc-day-content',true).first().getBox();
20965 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20966 cg.setWidth(ebox.right - sbox.x -2);
20968 cg.on('click', _this.onMoreEventClick, _this, c.more);
20978 onEventEnter: function (e, el,event,d) {
20979 this.fireEvent('evententer', this, el, event);
20982 onEventLeave: function (e, el,event,d) {
20983 this.fireEvent('eventleave', this, el, event);
20986 onEventClick: function (e, el,event,d) {
20987 this.fireEvent('eventclick', this, el, event);
20990 onMonthChange: function () {
20994 onMoreEventClick: function(e, el, more)
20998 this.calpopover.placement = 'right';
20999 this.calpopover.setTitle('More');
21001 this.calpopover.setContent('');
21003 var ctr = this.calpopover.el.select('.popover-content', true).first();
21005 Roo.each(more, function(m){
21007 cls : 'fc-event-hori fc-event-draggable',
21010 var cg = ctr.createChild(cfg);
21012 cg.on('click', _this.onEventClick, _this, m);
21015 this.calpopover.show(el);
21020 onLoad: function ()
21022 this.calevents = [];
21025 if(this.store.getCount() > 0){
21026 this.store.data.each(function(d){
21029 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21030 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21031 time : d.data.start_time,
21032 title : d.data.title,
21033 description : d.data.description,
21034 venue : d.data.venue
21039 this.renderEvents();
21041 if(this.calevents.length && this.loadMask){
21042 this.maskEl.hide();
21046 onBeforeLoad: function()
21048 this.clearEvents();
21050 this.maskEl.show();
21064 * @class Roo.bootstrap.Popover
21065 * @extends Roo.bootstrap.Component
21067 * Bootstrap Popover class
21068 * @cfg {String} html contents of the popover (or false to use children..)
21069 * @cfg {String} title of popover (or false to hide)
21070 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21071 * @cfg {String} trigger click || hover (or false to trigger manually)
21072 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21073 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21074 * - if false and it has a 'parent' then it will be automatically added to that element
21075 * - if string - Roo.get will be called
21076 * @cfg {Number} delay - delay before showing
21079 * Create a new Popover
21080 * @param {Object} config The config object
21083 Roo.bootstrap.Popover = function(config){
21084 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21090 * After the popover show
21092 * @param {Roo.bootstrap.Popover} this
21097 * After the popover hide
21099 * @param {Roo.bootstrap.Popover} this
21105 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21110 placement : 'right',
21111 trigger : 'hover', // hover
21117 can_build_overlaid : false,
21119 maskEl : false, // the mask element
21122 alignEl : false, // when show is called with an element - this get's stored.
21124 getChildContainer : function()
21126 return this.contentEl;
21129 getPopoverHeader : function()
21131 this.title = true; // flag not to hide it..
21132 this.headerEl.addClass('p-0');
21133 return this.headerEl
21137 getAutoCreate : function(){
21140 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21141 style: 'display:block',
21147 cls : 'popover-inner ',
21151 cls: 'popover-title popover-header',
21152 html : this.title === false ? '' : this.title
21155 cls : 'popover-content popover-body ' + (this.cls || ''),
21156 html : this.html || ''
21167 * @param {string} the title
21169 setTitle: function(str)
21173 this.headerEl.dom.innerHTML = str;
21178 * @param {string} the body content
21180 setContent: function(str)
21183 if (this.contentEl) {
21184 this.contentEl.dom.innerHTML = str;
21188 // as it get's added to the bottom of the page.
21189 onRender : function(ct, position)
21191 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21196 var cfg = Roo.apply({}, this.getAutoCreate());
21200 cfg.cls += ' ' + this.cls;
21203 cfg.style = this.style;
21205 //Roo.log("adding to ");
21206 this.el = Roo.get(document.body).createChild(cfg, position);
21207 // Roo.log(this.el);
21210 this.contentEl = this.el.select('.popover-content',true).first();
21211 this.headerEl = this.el.select('.popover-title',true).first();
21214 if(typeof(this.items) != 'undefined'){
21215 var items = this.items;
21218 for(var i =0;i < items.length;i++) {
21219 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21223 this.items = nitems;
21225 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21226 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21233 resizeMask : function()
21235 this.maskEl.setSize(
21236 Roo.lib.Dom.getViewWidth(true),
21237 Roo.lib.Dom.getViewHeight(true)
21241 initEvents : function()
21245 Roo.bootstrap.Popover.register(this);
21248 this.arrowEl = this.el.select('.arrow',true).first();
21249 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21250 this.el.enableDisplayMode('block');
21254 if (this.over === false && !this.parent()) {
21257 if (this.triggers === false) {
21262 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21263 var triggers = this.trigger ? this.trigger.split(' ') : [];
21264 Roo.each(triggers, function(trigger) {
21266 if (trigger == 'click') {
21267 on_el.on('click', this.toggle, this);
21268 } else if (trigger != 'manual') {
21269 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21270 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21272 on_el.on(eventIn ,this.enter, this);
21273 on_el.on(eventOut, this.leave, this);
21283 toggle : function () {
21284 this.hoverState == 'in' ? this.leave() : this.enter();
21287 enter : function () {
21289 clearTimeout(this.timeout);
21291 this.hoverState = 'in';
21293 if (!this.delay || !this.delay.show) {
21298 this.timeout = setTimeout(function () {
21299 if (_t.hoverState == 'in') {
21302 }, this.delay.show)
21305 leave : function() {
21306 clearTimeout(this.timeout);
21308 this.hoverState = 'out';
21310 if (!this.delay || !this.delay.hide) {
21315 this.timeout = setTimeout(function () {
21316 if (_t.hoverState == 'out') {
21319 }, this.delay.hide)
21323 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21324 * @param {string} (left|right|top|bottom) position
21326 show : function (on_el, placement)
21328 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21329 on_el = on_el || false; // default to false
21332 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21333 on_el = this.parent().el;
21334 } else if (this.over) {
21335 on_el = Roo.get(this.over);
21340 this.alignEl = Roo.get( on_el );
21343 this.render(document.body);
21349 if (this.title === false) {
21350 this.headerEl.hide();
21355 this.el.dom.style.display = 'block';
21358 if (this.alignEl) {
21359 this.updatePosition(this.placement, true);
21362 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21363 var es = this.el.getSize();
21364 var x = Roo.lib.Dom.getViewWidth()/2;
21365 var y = Roo.lib.Dom.getViewHeight()/2;
21366 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21371 //var arrow = this.el.select('.arrow',true).first();
21372 //arrow.set(align[2],
21374 this.el.addClass('in');
21378 this.hoverState = 'in';
21381 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21382 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21383 this.maskEl.dom.style.display = 'block';
21384 this.maskEl.addClass('show');
21386 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21388 this.fireEvent('show', this);
21392 * fire this manually after loading a grid in the table for example
21393 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21394 * @param {Boolean} try and move it if we cant get right position.
21396 updatePosition : function(placement, try_move)
21398 // allow for calling with no parameters
21399 placement = placement ? placement : this.placement;
21400 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21402 this.el.removeClass([
21403 'fade','top','bottom', 'left', 'right','in',
21404 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21406 this.el.addClass(placement + ' bs-popover-' + placement);
21408 if (!this.alignEl ) {
21412 switch (placement) {
21414 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21415 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21416 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21417 //normal display... or moved up/down.
21418 this.el.setXY(offset);
21419 var xy = this.alignEl.getAnchorXY('tr', false);
21421 this.arrowEl.setXY(xy);
21424 // continue through...
21425 return this.updatePosition('left', false);
21429 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21430 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21431 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21432 //normal display... or moved up/down.
21433 this.el.setXY(offset);
21434 var xy = this.alignEl.getAnchorXY('tl', false);
21435 xy[0]-=10;xy[1]+=5; // << fix me
21436 this.arrowEl.setXY(xy);
21440 return this.updatePosition('right', false);
21443 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21444 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21445 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21446 //normal display... or moved up/down.
21447 this.el.setXY(offset);
21448 var xy = this.alignEl.getAnchorXY('t', false);
21449 xy[1]-=10; // << fix me
21450 this.arrowEl.setXY(xy);
21454 return this.updatePosition('bottom', false);
21457 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21458 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21459 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21460 //normal display... or moved up/down.
21461 this.el.setXY(offset);
21462 var xy = this.alignEl.getAnchorXY('b', false);
21463 xy[1]+=2; // << fix me
21464 this.arrowEl.setXY(xy);
21468 return this.updatePosition('top', false);
21479 this.el.setXY([0,0]);
21480 this.el.removeClass('in');
21482 this.hoverState = null;
21483 this.maskEl.hide(); // always..
21484 this.fireEvent('hide', this);
21490 Roo.apply(Roo.bootstrap.Popover, {
21493 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21494 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21495 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21496 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21501 clickHander : false,
21505 onMouseDown : function(e)
21507 if (this.popups.length && !e.getTarget(".roo-popover")) {
21508 /// what is nothing is showing..
21517 register : function(popup)
21519 if (!Roo.bootstrap.Popover.clickHandler) {
21520 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21522 // hide other popups.
21523 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21524 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21525 this.hideAll(); //<< why?
21526 //this.popups.push(popup);
21528 hideAll : function()
21530 this.popups.forEach(function(p) {
21534 onShow : function() {
21535 Roo.bootstrap.Popover.popups.push(this);
21537 onHide : function() {
21538 Roo.bootstrap.Popover.popups.remove(this);
21544 * Card header - holder for the card header elements.
21549 * @class Roo.bootstrap.PopoverNav
21550 * @extends Roo.bootstrap.NavGroup
21551 * Bootstrap Popover header navigation class
21553 * Create a new Popover Header Navigation
21554 * @param {Object} config The config object
21557 Roo.bootstrap.PopoverNav = function(config){
21558 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21561 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21564 container_method : 'getPopoverHeader'
21582 * @class Roo.bootstrap.Progress
21583 * @extends Roo.bootstrap.Component
21584 * Bootstrap Progress class
21585 * @cfg {Boolean} striped striped of the progress bar
21586 * @cfg {Boolean} active animated of the progress bar
21590 * Create a new Progress
21591 * @param {Object} config The config object
21594 Roo.bootstrap.Progress = function(config){
21595 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21598 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21603 getAutoCreate : function(){
21611 cfg.cls += ' progress-striped';
21615 cfg.cls += ' active';
21634 * @class Roo.bootstrap.ProgressBar
21635 * @extends Roo.bootstrap.Component
21636 * Bootstrap ProgressBar class
21637 * @cfg {Number} aria_valuenow aria-value now
21638 * @cfg {Number} aria_valuemin aria-value min
21639 * @cfg {Number} aria_valuemax aria-value max
21640 * @cfg {String} label label for the progress bar
21641 * @cfg {String} panel (success | info | warning | danger )
21642 * @cfg {String} role role of the progress bar
21643 * @cfg {String} sr_only text
21647 * Create a new ProgressBar
21648 * @param {Object} config The config object
21651 Roo.bootstrap.ProgressBar = function(config){
21652 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21655 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21659 aria_valuemax : 100,
21665 getAutoCreate : function()
21670 cls: 'progress-bar',
21671 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21683 cfg.role = this.role;
21686 if(this.aria_valuenow){
21687 cfg['aria-valuenow'] = this.aria_valuenow;
21690 if(this.aria_valuemin){
21691 cfg['aria-valuemin'] = this.aria_valuemin;
21694 if(this.aria_valuemax){
21695 cfg['aria-valuemax'] = this.aria_valuemax;
21698 if(this.label && !this.sr_only){
21699 cfg.html = this.label;
21703 cfg.cls += ' progress-bar-' + this.panel;
21709 update : function(aria_valuenow)
21711 this.aria_valuenow = aria_valuenow;
21713 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21728 * @class Roo.bootstrap.TabGroup
21729 * @extends Roo.bootstrap.Column
21730 * Bootstrap Column class
21731 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21732 * @cfg {Boolean} carousel true to make the group behave like a carousel
21733 * @cfg {Boolean} bullets show bullets for the panels
21734 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21735 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21736 * @cfg {Boolean} showarrow (true|false) show arrow default true
21739 * Create a new TabGroup
21740 * @param {Object} config The config object
21743 Roo.bootstrap.TabGroup = function(config){
21744 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21746 this.navId = Roo.id();
21749 Roo.bootstrap.TabGroup.register(this);
21753 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21756 transition : false,
21761 slideOnTouch : false,
21764 getAutoCreate : function()
21766 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21768 cfg.cls += ' tab-content';
21770 if (this.carousel) {
21771 cfg.cls += ' carousel slide';
21774 cls : 'carousel-inner',
21778 if(this.bullets && !Roo.isTouch){
21781 cls : 'carousel-bullets',
21785 if(this.bullets_cls){
21786 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21793 cfg.cn[0].cn.push(bullets);
21796 if(this.showarrow){
21797 cfg.cn[0].cn.push({
21799 class : 'carousel-arrow',
21803 class : 'carousel-prev',
21807 class : 'fa fa-chevron-left'
21813 class : 'carousel-next',
21817 class : 'fa fa-chevron-right'
21830 initEvents: function()
21832 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21833 // this.el.on("touchstart", this.onTouchStart, this);
21836 if(this.autoslide){
21839 this.slideFn = window.setInterval(function() {
21840 _this.showPanelNext();
21844 if(this.showarrow){
21845 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21846 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21852 // onTouchStart : function(e, el, o)
21854 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21858 // this.showPanelNext();
21862 getChildContainer : function()
21864 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21868 * register a Navigation item
21869 * @param {Roo.bootstrap.NavItem} the navitem to add
21871 register : function(item)
21873 this.tabs.push( item);
21874 item.navId = this.navId; // not really needed..
21879 getActivePanel : function()
21882 Roo.each(this.tabs, function(t) {
21892 getPanelByName : function(n)
21895 Roo.each(this.tabs, function(t) {
21896 if (t.tabId == n) {
21904 indexOfPanel : function(p)
21907 Roo.each(this.tabs, function(t,i) {
21908 if (t.tabId == p.tabId) {
21917 * show a specific panel
21918 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21919 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21921 showPanel : function (pan)
21923 if(this.transition || typeof(pan) == 'undefined'){
21924 Roo.log("waiting for the transitionend");
21928 if (typeof(pan) == 'number') {
21929 pan = this.tabs[pan];
21932 if (typeof(pan) == 'string') {
21933 pan = this.getPanelByName(pan);
21936 var cur = this.getActivePanel();
21939 Roo.log('pan or acitve pan is undefined');
21943 if (pan.tabId == this.getActivePanel().tabId) {
21947 if (false === cur.fireEvent('beforedeactivate')) {
21951 if(this.bullets > 0 && !Roo.isTouch){
21952 this.setActiveBullet(this.indexOfPanel(pan));
21955 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21957 //class="carousel-item carousel-item-next carousel-item-left"
21959 this.transition = true;
21960 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21961 var lr = dir == 'next' ? 'left' : 'right';
21962 pan.el.addClass(dir); // or prev
21963 pan.el.addClass('carousel-item-' + dir); // or prev
21964 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21965 cur.el.addClass(lr); // or right
21966 pan.el.addClass(lr);
21967 cur.el.addClass('carousel-item-' +lr); // or right
21968 pan.el.addClass('carousel-item-' +lr);
21972 cur.el.on('transitionend', function() {
21973 Roo.log("trans end?");
21975 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21976 pan.setActive(true);
21978 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21979 cur.setActive(false);
21981 _this.transition = false;
21983 }, this, { single: true } );
21988 cur.setActive(false);
21989 pan.setActive(true);
21994 showPanelNext : function()
21996 var i = this.indexOfPanel(this.getActivePanel());
21998 if (i >= this.tabs.length - 1 && !this.autoslide) {
22002 if (i >= this.tabs.length - 1 && this.autoslide) {
22006 this.showPanel(this.tabs[i+1]);
22009 showPanelPrev : function()
22011 var i = this.indexOfPanel(this.getActivePanel());
22013 if (i < 1 && !this.autoslide) {
22017 if (i < 1 && this.autoslide) {
22018 i = this.tabs.length;
22021 this.showPanel(this.tabs[i-1]);
22025 addBullet: function()
22027 if(!this.bullets || Roo.isTouch){
22030 var ctr = this.el.select('.carousel-bullets',true).first();
22031 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22032 var bullet = ctr.createChild({
22033 cls : 'bullet bullet-' + i
22034 },ctr.dom.lastChild);
22039 bullet.on('click', (function(e, el, o, ii, t){
22041 e.preventDefault();
22043 this.showPanel(ii);
22045 if(this.autoslide && this.slideFn){
22046 clearInterval(this.slideFn);
22047 this.slideFn = window.setInterval(function() {
22048 _this.showPanelNext();
22052 }).createDelegate(this, [i, bullet], true));
22057 setActiveBullet : function(i)
22063 Roo.each(this.el.select('.bullet', true).elements, function(el){
22064 el.removeClass('selected');
22067 var bullet = this.el.select('.bullet-' + i, true).first();
22073 bullet.addClass('selected');
22084 Roo.apply(Roo.bootstrap.TabGroup, {
22088 * register a Navigation Group
22089 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22091 register : function(navgrp)
22093 this.groups[navgrp.navId] = navgrp;
22097 * fetch a Navigation Group based on the navigation ID
22098 * if one does not exist , it will get created.
22099 * @param {string} the navgroup to add
22100 * @returns {Roo.bootstrap.NavGroup} the navgroup
22102 get: function(navId) {
22103 if (typeof(this.groups[navId]) == 'undefined') {
22104 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22106 return this.groups[navId] ;
22121 * @class Roo.bootstrap.TabPanel
22122 * @extends Roo.bootstrap.Component
22123 * Bootstrap TabPanel class
22124 * @cfg {Boolean} active panel active
22125 * @cfg {String} html panel content
22126 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22127 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22128 * @cfg {String} href click to link..
22129 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22133 * Create a new TabPanel
22134 * @param {Object} config The config object
22137 Roo.bootstrap.TabPanel = function(config){
22138 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22142 * Fires when the active status changes
22143 * @param {Roo.bootstrap.TabPanel} this
22144 * @param {Boolean} state the new state
22149 * @event beforedeactivate
22150 * Fires before a tab is de-activated - can be used to do validation on a form.
22151 * @param {Roo.bootstrap.TabPanel} this
22152 * @return {Boolean} false if there is an error
22155 'beforedeactivate': true
22158 this.tabId = this.tabId || Roo.id();
22162 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22169 touchSlide : false,
22170 getAutoCreate : function(){
22175 // item is needed for carousel - not sure if it has any effect otherwise
22176 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22177 html: this.html || ''
22181 cfg.cls += ' active';
22185 cfg.tabId = this.tabId;
22193 initEvents: function()
22195 var p = this.parent();
22197 this.navId = this.navId || p.navId;
22199 if (typeof(this.navId) != 'undefined') {
22200 // not really needed.. but just in case.. parent should be a NavGroup.
22201 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22205 var i = tg.tabs.length - 1;
22207 if(this.active && tg.bullets > 0 && i < tg.bullets){
22208 tg.setActiveBullet(i);
22212 this.el.on('click', this.onClick, this);
22214 if(Roo.isTouch && this.touchSlide){
22215 this.el.on("touchstart", this.onTouchStart, this);
22216 this.el.on("touchmove", this.onTouchMove, this);
22217 this.el.on("touchend", this.onTouchEnd, this);
22222 onRender : function(ct, position)
22224 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22227 setActive : function(state)
22229 Roo.log("panel - set active " + this.tabId + "=" + state);
22231 this.active = state;
22233 this.el.removeClass('active');
22235 } else if (!this.el.hasClass('active')) {
22236 this.el.addClass('active');
22239 this.fireEvent('changed', this, state);
22242 onClick : function(e)
22244 e.preventDefault();
22246 if(!this.href.length){
22250 window.location.href = this.href;
22259 onTouchStart : function(e)
22261 this.swiping = false;
22263 this.startX = e.browserEvent.touches[0].clientX;
22264 this.startY = e.browserEvent.touches[0].clientY;
22267 onTouchMove : function(e)
22269 this.swiping = true;
22271 this.endX = e.browserEvent.touches[0].clientX;
22272 this.endY = e.browserEvent.touches[0].clientY;
22275 onTouchEnd : function(e)
22282 var tabGroup = this.parent();
22284 if(this.endX > this.startX){ // swiping right
22285 tabGroup.showPanelPrev();
22289 if(this.startX > this.endX){ // swiping left
22290 tabGroup.showPanelNext();
22309 * @class Roo.bootstrap.DateField
22310 * @extends Roo.bootstrap.Input
22311 * Bootstrap DateField class
22312 * @cfg {Number} weekStart default 0
22313 * @cfg {String} viewMode default empty, (months|years)
22314 * @cfg {String} minViewMode default empty, (months|years)
22315 * @cfg {Number} startDate default -Infinity
22316 * @cfg {Number} endDate default Infinity
22317 * @cfg {Boolean} todayHighlight default false
22318 * @cfg {Boolean} todayBtn default false
22319 * @cfg {Boolean} calendarWeeks default false
22320 * @cfg {Object} daysOfWeekDisabled default empty
22321 * @cfg {Boolean} singleMode default false (true | false)
22323 * @cfg {Boolean} keyboardNavigation default true
22324 * @cfg {String} language default en
22327 * Create a new DateField
22328 * @param {Object} config The config object
22331 Roo.bootstrap.DateField = function(config){
22332 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22336 * Fires when this field show.
22337 * @param {Roo.bootstrap.DateField} this
22338 * @param {Mixed} date The date value
22343 * Fires when this field hide.
22344 * @param {Roo.bootstrap.DateField} this
22345 * @param {Mixed} date The date value
22350 * Fires when select a date.
22351 * @param {Roo.bootstrap.DateField} this
22352 * @param {Mixed} date The date value
22356 * @event beforeselect
22357 * Fires when before select a date.
22358 * @param {Roo.bootstrap.DateField} this
22359 * @param {Mixed} date The date value
22361 beforeselect : true
22365 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22368 * @cfg {String} format
22369 * The default date format string which can be overriden for localization support. The format must be
22370 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22374 * @cfg {String} altFormats
22375 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22376 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22378 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22386 todayHighlight : false,
22392 keyboardNavigation: true,
22394 calendarWeeks: false,
22396 startDate: -Infinity,
22400 daysOfWeekDisabled: [],
22404 singleMode : false,
22406 UTCDate: function()
22408 return new Date(Date.UTC.apply(Date, arguments));
22411 UTCToday: function()
22413 var today = new Date();
22414 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22417 getDate: function() {
22418 var d = this.getUTCDate();
22419 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22422 getUTCDate: function() {
22426 setDate: function(d) {
22427 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22430 setUTCDate: function(d) {
22432 this.setValue(this.formatDate(this.date));
22435 onRender: function(ct, position)
22438 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22440 this.language = this.language || 'en';
22441 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22442 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22444 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22445 this.format = this.format || 'm/d/y';
22446 this.isInline = false;
22447 this.isInput = true;
22448 this.component = this.el.select('.add-on', true).first() || false;
22449 this.component = (this.component && this.component.length === 0) ? false : this.component;
22450 this.hasInput = this.component && this.inputEl().length;
22452 if (typeof(this.minViewMode === 'string')) {
22453 switch (this.minViewMode) {
22455 this.minViewMode = 1;
22458 this.minViewMode = 2;
22461 this.minViewMode = 0;
22466 if (typeof(this.viewMode === 'string')) {
22467 switch (this.viewMode) {
22480 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22482 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22484 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22486 this.picker().on('mousedown', this.onMousedown, this);
22487 this.picker().on('click', this.onClick, this);
22489 this.picker().addClass('datepicker-dropdown');
22491 this.startViewMode = this.viewMode;
22493 if(this.singleMode){
22494 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22495 v.setVisibilityMode(Roo.Element.DISPLAY);
22499 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22500 v.setStyle('width', '189px');
22504 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22505 if(!this.calendarWeeks){
22510 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22511 v.attr('colspan', function(i, val){
22512 return parseInt(val) + 1;
22517 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22519 this.setStartDate(this.startDate);
22520 this.setEndDate(this.endDate);
22522 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22529 if(this.isInline) {
22534 picker : function()
22536 return this.pickerEl;
22537 // return this.el.select('.datepicker', true).first();
22540 fillDow: function()
22542 var dowCnt = this.weekStart;
22551 if(this.calendarWeeks){
22559 while (dowCnt < this.weekStart + 7) {
22563 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22567 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22570 fillMonths: function()
22573 var months = this.picker().select('>.datepicker-months td', true).first();
22575 months.dom.innerHTML = '';
22581 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22584 months.createChild(month);
22591 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;
22593 if (this.date < this.startDate) {
22594 this.viewDate = new Date(this.startDate);
22595 } else if (this.date > this.endDate) {
22596 this.viewDate = new Date(this.endDate);
22598 this.viewDate = new Date(this.date);
22606 var d = new Date(this.viewDate),
22607 year = d.getUTCFullYear(),
22608 month = d.getUTCMonth(),
22609 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22610 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22611 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22612 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22613 currentDate = this.date && this.date.valueOf(),
22614 today = this.UTCToday();
22616 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22618 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22620 // this.picker.select('>tfoot th.today').
22621 // .text(dates[this.language].today)
22622 // .toggle(this.todayBtn !== false);
22624 this.updateNavArrows();
22627 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22629 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22631 prevMonth.setUTCDate(day);
22633 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22635 var nextMonth = new Date(prevMonth);
22637 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22639 nextMonth = nextMonth.valueOf();
22641 var fillMonths = false;
22643 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22645 while(prevMonth.valueOf() <= nextMonth) {
22648 if (prevMonth.getUTCDay() === this.weekStart) {
22650 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22658 if(this.calendarWeeks){
22659 // ISO 8601: First week contains first thursday.
22660 // ISO also states week starts on Monday, but we can be more abstract here.
22662 // Start of current week: based on weekstart/current date
22663 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22664 // Thursday of this week
22665 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22666 // First Thursday of year, year from thursday
22667 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22668 // Calendar week: ms between thursdays, div ms per day, div 7 days
22669 calWeek = (th - yth) / 864e5 / 7 + 1;
22671 fillMonths.cn.push({
22679 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22681 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22684 if (this.todayHighlight &&
22685 prevMonth.getUTCFullYear() == today.getFullYear() &&
22686 prevMonth.getUTCMonth() == today.getMonth() &&
22687 prevMonth.getUTCDate() == today.getDate()) {
22688 clsName += ' today';
22691 if (currentDate && prevMonth.valueOf() === currentDate) {
22692 clsName += ' active';
22695 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22696 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22697 clsName += ' disabled';
22700 fillMonths.cn.push({
22702 cls: 'day ' + clsName,
22703 html: prevMonth.getDate()
22706 prevMonth.setDate(prevMonth.getDate()+1);
22709 var currentYear = this.date && this.date.getUTCFullYear();
22710 var currentMonth = this.date && this.date.getUTCMonth();
22712 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22714 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22715 v.removeClass('active');
22717 if(currentYear === year && k === currentMonth){
22718 v.addClass('active');
22721 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22722 v.addClass('disabled');
22728 year = parseInt(year/10, 10) * 10;
22730 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22732 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22735 for (var i = -1; i < 11; i++) {
22736 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22738 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22746 showMode: function(dir)
22749 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22752 Roo.each(this.picker().select('>div',true).elements, function(v){
22753 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22756 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22761 if(this.isInline) {
22765 this.picker().removeClass(['bottom', 'top']);
22767 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22769 * place to the top of element!
22773 this.picker().addClass('top');
22774 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22779 this.picker().addClass('bottom');
22781 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22784 parseDate : function(value)
22786 if(!value || value instanceof Date){
22789 var v = Date.parseDate(value, this.format);
22790 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22791 v = Date.parseDate(value, 'Y-m-d');
22793 if(!v && this.altFormats){
22794 if(!this.altFormatsArray){
22795 this.altFormatsArray = this.altFormats.split("|");
22797 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22798 v = Date.parseDate(value, this.altFormatsArray[i]);
22804 formatDate : function(date, fmt)
22806 return (!date || !(date instanceof Date)) ?
22807 date : date.dateFormat(fmt || this.format);
22810 onFocus : function()
22812 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22816 onBlur : function()
22818 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22820 var d = this.inputEl().getValue();
22827 showPopup : function()
22829 this.picker().show();
22833 this.fireEvent('showpopup', this, this.date);
22836 hidePopup : function()
22838 if(this.isInline) {
22841 this.picker().hide();
22842 this.viewMode = this.startViewMode;
22845 this.fireEvent('hidepopup', this, this.date);
22849 onMousedown: function(e)
22851 e.stopPropagation();
22852 e.preventDefault();
22857 Roo.bootstrap.DateField.superclass.keyup.call(this);
22861 setValue: function(v)
22863 if(this.fireEvent('beforeselect', this, v) !== false){
22864 var d = new Date(this.parseDate(v) ).clearTime();
22866 if(isNaN(d.getTime())){
22867 this.date = this.viewDate = '';
22868 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22872 v = this.formatDate(d);
22874 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22876 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22880 this.fireEvent('select', this, this.date);
22884 getValue: function()
22886 return this.formatDate(this.date);
22889 fireKey: function(e)
22891 if (!this.picker().isVisible()){
22892 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22898 var dateChanged = false,
22900 newDate, newViewDate;
22905 e.preventDefault();
22909 if (!this.keyboardNavigation) {
22912 dir = e.keyCode == 37 ? -1 : 1;
22915 newDate = this.moveYear(this.date, dir);
22916 newViewDate = this.moveYear(this.viewDate, dir);
22917 } else if (e.shiftKey){
22918 newDate = this.moveMonth(this.date, dir);
22919 newViewDate = this.moveMonth(this.viewDate, dir);
22921 newDate = new Date(this.date);
22922 newDate.setUTCDate(this.date.getUTCDate() + dir);
22923 newViewDate = new Date(this.viewDate);
22924 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22926 if (this.dateWithinRange(newDate)){
22927 this.date = newDate;
22928 this.viewDate = newViewDate;
22929 this.setValue(this.formatDate(this.date));
22931 e.preventDefault();
22932 dateChanged = true;
22937 if (!this.keyboardNavigation) {
22940 dir = e.keyCode == 38 ? -1 : 1;
22942 newDate = this.moveYear(this.date, dir);
22943 newViewDate = this.moveYear(this.viewDate, dir);
22944 } else if (e.shiftKey){
22945 newDate = this.moveMonth(this.date, dir);
22946 newViewDate = this.moveMonth(this.viewDate, dir);
22948 newDate = new Date(this.date);
22949 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22950 newViewDate = new Date(this.viewDate);
22951 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22953 if (this.dateWithinRange(newDate)){
22954 this.date = newDate;
22955 this.viewDate = newViewDate;
22956 this.setValue(this.formatDate(this.date));
22958 e.preventDefault();
22959 dateChanged = true;
22963 this.setValue(this.formatDate(this.date));
22965 e.preventDefault();
22968 this.setValue(this.formatDate(this.date));
22982 onClick: function(e)
22984 e.stopPropagation();
22985 e.preventDefault();
22987 var target = e.getTarget();
22989 if(target.nodeName.toLowerCase() === 'i'){
22990 target = Roo.get(target).dom.parentNode;
22993 var nodeName = target.nodeName;
22994 var className = target.className;
22995 var html = target.innerHTML;
22996 //Roo.log(nodeName);
22998 switch(nodeName.toLowerCase()) {
23000 switch(className) {
23006 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23007 switch(this.viewMode){
23009 this.viewDate = this.moveMonth(this.viewDate, dir);
23013 this.viewDate = this.moveYear(this.viewDate, dir);
23019 var date = new Date();
23020 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23022 this.setValue(this.formatDate(this.date));
23029 if (className.indexOf('disabled') < 0) {
23030 if (!this.viewDate) {
23031 this.viewDate = new Date();
23033 this.viewDate.setUTCDate(1);
23034 if (className.indexOf('month') > -1) {
23035 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23037 var year = parseInt(html, 10) || 0;
23038 this.viewDate.setUTCFullYear(year);
23042 if(this.singleMode){
23043 this.setValue(this.formatDate(this.viewDate));
23054 //Roo.log(className);
23055 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23056 var day = parseInt(html, 10) || 1;
23057 var year = (this.viewDate || new Date()).getUTCFullYear(),
23058 month = (this.viewDate || new Date()).getUTCMonth();
23060 if (className.indexOf('old') > -1) {
23067 } else if (className.indexOf('new') > -1) {
23075 //Roo.log([year,month,day]);
23076 this.date = this.UTCDate(year, month, day,0,0,0,0);
23077 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23079 //Roo.log(this.formatDate(this.date));
23080 this.setValue(this.formatDate(this.date));
23087 setStartDate: function(startDate)
23089 this.startDate = startDate || -Infinity;
23090 if (this.startDate !== -Infinity) {
23091 this.startDate = this.parseDate(this.startDate);
23094 this.updateNavArrows();
23097 setEndDate: function(endDate)
23099 this.endDate = endDate || Infinity;
23100 if (this.endDate !== Infinity) {
23101 this.endDate = this.parseDate(this.endDate);
23104 this.updateNavArrows();
23107 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23109 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23110 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23111 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23113 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23114 return parseInt(d, 10);
23117 this.updateNavArrows();
23120 updateNavArrows: function()
23122 if(this.singleMode){
23126 var d = new Date(this.viewDate),
23127 year = d.getUTCFullYear(),
23128 month = d.getUTCMonth();
23130 Roo.each(this.picker().select('.prev', true).elements, function(v){
23132 switch (this.viewMode) {
23135 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23141 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23148 Roo.each(this.picker().select('.next', true).elements, function(v){
23150 switch (this.viewMode) {
23153 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23159 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23167 moveMonth: function(date, dir)
23172 var new_date = new Date(date.valueOf()),
23173 day = new_date.getUTCDate(),
23174 month = new_date.getUTCMonth(),
23175 mag = Math.abs(dir),
23177 dir = dir > 0 ? 1 : -1;
23180 // If going back one month, make sure month is not current month
23181 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23183 return new_date.getUTCMonth() == month;
23185 // If going forward one month, make sure month is as expected
23186 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23188 return new_date.getUTCMonth() != new_month;
23190 new_month = month + dir;
23191 new_date.setUTCMonth(new_month);
23192 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23193 if (new_month < 0 || new_month > 11) {
23194 new_month = (new_month + 12) % 12;
23197 // For magnitudes >1, move one month at a time...
23198 for (var i=0; i<mag; i++) {
23199 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23200 new_date = this.moveMonth(new_date, dir);
23202 // ...then reset the day, keeping it in the new month
23203 new_month = new_date.getUTCMonth();
23204 new_date.setUTCDate(day);
23206 return new_month != new_date.getUTCMonth();
23209 // Common date-resetting loop -- if date is beyond end of month, make it
23212 new_date.setUTCDate(--day);
23213 new_date.setUTCMonth(new_month);
23218 moveYear: function(date, dir)
23220 return this.moveMonth(date, dir*12);
23223 dateWithinRange: function(date)
23225 return date >= this.startDate && date <= this.endDate;
23231 this.picker().remove();
23234 validateValue : function(value)
23236 if(this.getVisibilityEl().hasClass('hidden')){
23240 if(value.length < 1) {
23241 if(this.allowBlank){
23247 if(value.length < this.minLength){
23250 if(value.length > this.maxLength){
23254 var vt = Roo.form.VTypes;
23255 if(!vt[this.vtype](value, this)){
23259 if(typeof this.validator == "function"){
23260 var msg = this.validator(value);
23266 if(this.regex && !this.regex.test(value)){
23270 if(typeof(this.parseDate(value)) == 'undefined'){
23274 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23278 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23288 this.date = this.viewDate = '';
23290 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23295 Roo.apply(Roo.bootstrap.DateField, {
23306 html: '<i class="fa fa-arrow-left"/>'
23316 html: '<i class="fa fa-arrow-right"/>'
23358 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23359 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23360 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23361 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23362 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23375 navFnc: 'FullYear',
23380 navFnc: 'FullYear',
23385 Roo.apply(Roo.bootstrap.DateField, {
23389 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23393 cls: 'datepicker-days',
23397 cls: 'table-condensed',
23399 Roo.bootstrap.DateField.head,
23403 Roo.bootstrap.DateField.footer
23410 cls: 'datepicker-months',
23414 cls: 'table-condensed',
23416 Roo.bootstrap.DateField.head,
23417 Roo.bootstrap.DateField.content,
23418 Roo.bootstrap.DateField.footer
23425 cls: 'datepicker-years',
23429 cls: 'table-condensed',
23431 Roo.bootstrap.DateField.head,
23432 Roo.bootstrap.DateField.content,
23433 Roo.bootstrap.DateField.footer
23452 * @class Roo.bootstrap.TimeField
23453 * @extends Roo.bootstrap.Input
23454 * Bootstrap DateField class
23458 * Create a new TimeField
23459 * @param {Object} config The config object
23462 Roo.bootstrap.TimeField = function(config){
23463 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23467 * Fires when this field show.
23468 * @param {Roo.bootstrap.DateField} thisthis
23469 * @param {Mixed} date The date value
23474 * Fires when this field hide.
23475 * @param {Roo.bootstrap.DateField} this
23476 * @param {Mixed} date The date value
23481 * Fires when select a date.
23482 * @param {Roo.bootstrap.DateField} this
23483 * @param {Mixed} date The date value
23489 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23492 * @cfg {String} format
23493 * The default time format string which can be overriden for localization support. The format must be
23494 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23498 getAutoCreate : function()
23500 this.after = '<i class="fa far fa-clock"></i>';
23501 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23505 onRender: function(ct, position)
23508 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23510 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23512 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23514 this.pop = this.picker().select('>.datepicker-time',true).first();
23515 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23517 this.picker().on('mousedown', this.onMousedown, this);
23518 this.picker().on('click', this.onClick, this);
23520 this.picker().addClass('datepicker-dropdown');
23525 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23526 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23527 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23528 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23529 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23530 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23534 fireKey: function(e){
23535 if (!this.picker().isVisible()){
23536 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23542 e.preventDefault();
23550 this.onTogglePeriod();
23553 this.onIncrementMinutes();
23556 this.onDecrementMinutes();
23565 onClick: function(e) {
23566 e.stopPropagation();
23567 e.preventDefault();
23570 picker : function()
23572 return this.pickerEl;
23575 fillTime: function()
23577 var time = this.pop.select('tbody', true).first();
23579 time.dom.innerHTML = '';
23594 cls: 'hours-up fa fas fa-chevron-up'
23614 cls: 'minutes-up fa fas fa-chevron-up'
23635 cls: 'timepicker-hour',
23650 cls: 'timepicker-minute',
23665 cls: 'btn btn-primary period',
23687 cls: 'hours-down fa fas fa-chevron-down'
23707 cls: 'minutes-down fa fas fa-chevron-down'
23725 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23732 var hours = this.time.getHours();
23733 var minutes = this.time.getMinutes();
23746 hours = hours - 12;
23750 hours = '0' + hours;
23754 minutes = '0' + minutes;
23757 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23758 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23759 this.pop.select('button', true).first().dom.innerHTML = period;
23765 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23767 var cls = ['bottom'];
23769 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23776 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23780 //this.picker().setXY(20000,20000);
23781 this.picker().addClass(cls.join('-'));
23785 Roo.each(cls, function(c){
23790 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23791 //_this.picker().setTop(_this.inputEl().getHeight());
23795 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23797 //_this.picker().setTop(0 - _this.picker().getHeight());
23802 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23806 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23814 onFocus : function()
23816 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23820 onBlur : function()
23822 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23828 this.picker().show();
23833 this.fireEvent('show', this, this.date);
23838 this.picker().hide();
23841 this.fireEvent('hide', this, this.date);
23844 setTime : function()
23847 this.setValue(this.time.format(this.format));
23849 this.fireEvent('select', this, this.date);
23854 onMousedown: function(e){
23855 e.stopPropagation();
23856 e.preventDefault();
23859 onIncrementHours: function()
23861 Roo.log('onIncrementHours');
23862 this.time = this.time.add(Date.HOUR, 1);
23867 onDecrementHours: function()
23869 Roo.log('onDecrementHours');
23870 this.time = this.time.add(Date.HOUR, -1);
23874 onIncrementMinutes: function()
23876 Roo.log('onIncrementMinutes');
23877 this.time = this.time.add(Date.MINUTE, 1);
23881 onDecrementMinutes: function()
23883 Roo.log('onDecrementMinutes');
23884 this.time = this.time.add(Date.MINUTE, -1);
23888 onTogglePeriod: function()
23890 Roo.log('onTogglePeriod');
23891 this.time = this.time.add(Date.HOUR, 12);
23899 Roo.apply(Roo.bootstrap.TimeField, {
23903 cls: 'datepicker dropdown-menu',
23907 cls: 'datepicker-time',
23911 cls: 'table-condensed',
23940 cls: 'btn btn-info ok',
23968 * @class Roo.bootstrap.MonthField
23969 * @extends Roo.bootstrap.Input
23970 * Bootstrap MonthField class
23972 * @cfg {String} language default en
23975 * Create a new MonthField
23976 * @param {Object} config The config object
23979 Roo.bootstrap.MonthField = function(config){
23980 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23985 * Fires when this field show.
23986 * @param {Roo.bootstrap.MonthField} this
23987 * @param {Mixed} date The date value
23992 * Fires when this field hide.
23993 * @param {Roo.bootstrap.MonthField} this
23994 * @param {Mixed} date The date value
23999 * Fires when select a date.
24000 * @param {Roo.bootstrap.MonthField} this
24001 * @param {String} oldvalue The old value
24002 * @param {String} newvalue The new value
24008 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24010 onRender: function(ct, position)
24013 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24015 this.language = this.language || 'en';
24016 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24017 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24019 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24020 this.isInline = false;
24021 this.isInput = true;
24022 this.component = this.el.select('.add-on', true).first() || false;
24023 this.component = (this.component && this.component.length === 0) ? false : this.component;
24024 this.hasInput = this.component && this.inputEL().length;
24026 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24028 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24030 this.picker().on('mousedown', this.onMousedown, this);
24031 this.picker().on('click', this.onClick, this);
24033 this.picker().addClass('datepicker-dropdown');
24035 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24036 v.setStyle('width', '189px');
24043 if(this.isInline) {
24049 setValue: function(v, suppressEvent)
24051 var o = this.getValue();
24053 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24057 if(suppressEvent !== true){
24058 this.fireEvent('select', this, o, v);
24063 getValue: function()
24068 onClick: function(e)
24070 e.stopPropagation();
24071 e.preventDefault();
24073 var target = e.getTarget();
24075 if(target.nodeName.toLowerCase() === 'i'){
24076 target = Roo.get(target).dom.parentNode;
24079 var nodeName = target.nodeName;
24080 var className = target.className;
24081 var html = target.innerHTML;
24083 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24087 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24089 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24095 picker : function()
24097 return this.pickerEl;
24100 fillMonths: function()
24103 var months = this.picker().select('>.datepicker-months td', true).first();
24105 months.dom.innerHTML = '';
24111 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24114 months.createChild(month);
24123 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24124 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24127 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24128 e.removeClass('active');
24130 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24131 e.addClass('active');
24138 if(this.isInline) {
24142 this.picker().removeClass(['bottom', 'top']);
24144 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24146 * place to the top of element!
24150 this.picker().addClass('top');
24151 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24156 this.picker().addClass('bottom');
24158 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24161 onFocus : function()
24163 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24167 onBlur : function()
24169 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24171 var d = this.inputEl().getValue();
24180 this.picker().show();
24181 this.picker().select('>.datepicker-months', true).first().show();
24185 this.fireEvent('show', this, this.date);
24190 if(this.isInline) {
24193 this.picker().hide();
24194 this.fireEvent('hide', this, this.date);
24198 onMousedown: function(e)
24200 e.stopPropagation();
24201 e.preventDefault();
24206 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24210 fireKey: function(e)
24212 if (!this.picker().isVisible()){
24213 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24224 e.preventDefault();
24228 dir = e.keyCode == 37 ? -1 : 1;
24230 this.vIndex = this.vIndex + dir;
24232 if(this.vIndex < 0){
24236 if(this.vIndex > 11){
24240 if(isNaN(this.vIndex)){
24244 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24250 dir = e.keyCode == 38 ? -1 : 1;
24252 this.vIndex = this.vIndex + dir * 4;
24254 if(this.vIndex < 0){
24258 if(this.vIndex > 11){
24262 if(isNaN(this.vIndex)){
24266 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24271 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24272 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24276 e.preventDefault();
24279 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24280 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24296 this.picker().remove();
24301 Roo.apply(Roo.bootstrap.MonthField, {
24320 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24321 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24326 Roo.apply(Roo.bootstrap.MonthField, {
24330 cls: 'datepicker dropdown-menu roo-dynamic',
24334 cls: 'datepicker-months',
24338 cls: 'table-condensed',
24340 Roo.bootstrap.DateField.content
24360 * @class Roo.bootstrap.CheckBox
24361 * @extends Roo.bootstrap.Input
24362 * Bootstrap CheckBox class
24364 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24365 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24366 * @cfg {String} boxLabel The text that appears beside the checkbox
24367 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24368 * @cfg {Boolean} checked initnal the element
24369 * @cfg {Boolean} inline inline the element (default false)
24370 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24371 * @cfg {String} tooltip label tooltip
24374 * Create a new CheckBox
24375 * @param {Object} config The config object
24378 Roo.bootstrap.CheckBox = function(config){
24379 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24384 * Fires when the element is checked or unchecked.
24385 * @param {Roo.bootstrap.CheckBox} this This input
24386 * @param {Boolean} checked The new checked value
24391 * Fires when the element is click.
24392 * @param {Roo.bootstrap.CheckBox} this This input
24399 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24401 inputType: 'checkbox',
24410 // checkbox success does not make any sense really..
24415 getAutoCreate : function()
24417 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24423 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24426 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24432 type : this.inputType,
24433 value : this.inputValue,
24434 cls : 'roo-' + this.inputType, //'form-box',
24435 placeholder : this.placeholder || ''
24439 if(this.inputType != 'radio'){
24443 cls : 'roo-hidden-value',
24444 value : this.checked ? this.inputValue : this.valueOff
24449 if (this.weight) { // Validity check?
24450 cfg.cls += " " + this.inputType + "-" + this.weight;
24453 if (this.disabled) {
24454 input.disabled=true;
24458 input.checked = this.checked;
24463 input.name = this.name;
24465 if(this.inputType != 'radio'){
24466 hidden.name = this.name;
24467 input.name = '_hidden_' + this.name;
24472 input.cls += ' input-' + this.size;
24477 ['xs','sm','md','lg'].map(function(size){
24478 if (settings[size]) {
24479 cfg.cls += ' col-' + size + '-' + settings[size];
24483 var inputblock = input;
24485 if (this.before || this.after) {
24488 cls : 'input-group',
24493 inputblock.cn.push({
24495 cls : 'input-group-addon',
24500 inputblock.cn.push(input);
24502 if(this.inputType != 'radio'){
24503 inputblock.cn.push(hidden);
24507 inputblock.cn.push({
24509 cls : 'input-group-addon',
24515 var boxLabelCfg = false;
24521 //'for': id, // box label is handled by onclick - so no for...
24523 html: this.boxLabel
24526 boxLabelCfg.tooltip = this.tooltip;
24532 if (align ==='left' && this.fieldLabel.length) {
24533 // Roo.log("left and has label");
24538 cls : 'control-label',
24539 html : this.fieldLabel
24550 cfg.cn[1].cn.push(boxLabelCfg);
24553 if(this.labelWidth > 12){
24554 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24557 if(this.labelWidth < 13 && this.labelmd == 0){
24558 this.labelmd = this.labelWidth;
24561 if(this.labellg > 0){
24562 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24563 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24566 if(this.labelmd > 0){
24567 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24568 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24571 if(this.labelsm > 0){
24572 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24573 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24576 if(this.labelxs > 0){
24577 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24578 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24581 } else if ( this.fieldLabel.length) {
24582 // Roo.log(" label");
24586 tag: this.boxLabel ? 'span' : 'label',
24588 cls: 'control-label box-input-label',
24589 //cls : 'input-group-addon',
24590 html : this.fieldLabel
24597 cfg.cn.push(boxLabelCfg);
24602 // Roo.log(" no label && no align");
24603 cfg.cn = [ inputblock ] ;
24605 cfg.cn.push(boxLabelCfg);
24613 if(this.inputType != 'radio'){
24614 cfg.cn.push(hidden);
24622 * return the real input element.
24624 inputEl: function ()
24626 return this.el.select('input.roo-' + this.inputType,true).first();
24628 hiddenEl: function ()
24630 return this.el.select('input.roo-hidden-value',true).first();
24633 labelEl: function()
24635 return this.el.select('label.control-label',true).first();
24637 /* depricated... */
24641 return this.labelEl();
24644 boxLabelEl: function()
24646 return this.el.select('label.box-label',true).first();
24649 initEvents : function()
24651 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24653 this.inputEl().on('click', this.onClick, this);
24655 if (this.boxLabel) {
24656 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24659 this.startValue = this.getValue();
24662 Roo.bootstrap.CheckBox.register(this);
24666 onClick : function(e)
24668 if(this.fireEvent('click', this, e) !== false){
24669 this.setChecked(!this.checked);
24674 setChecked : function(state,suppressEvent)
24676 this.startValue = this.getValue();
24678 if(this.inputType == 'radio'){
24680 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24681 e.dom.checked = false;
24684 this.inputEl().dom.checked = true;
24686 this.inputEl().dom.value = this.inputValue;
24688 if(suppressEvent !== true){
24689 this.fireEvent('check', this, true);
24697 this.checked = state;
24699 this.inputEl().dom.checked = state;
24702 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24704 if(suppressEvent !== true){
24705 this.fireEvent('check', this, state);
24711 getValue : function()
24713 if(this.inputType == 'radio'){
24714 return this.getGroupValue();
24717 return this.hiddenEl().dom.value;
24721 getGroupValue : function()
24723 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24727 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24730 setValue : function(v,suppressEvent)
24732 if(this.inputType == 'radio'){
24733 this.setGroupValue(v, suppressEvent);
24737 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24742 setGroupValue : function(v, suppressEvent)
24744 this.startValue = this.getValue();
24746 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24747 e.dom.checked = false;
24749 if(e.dom.value == v){
24750 e.dom.checked = true;
24754 if(suppressEvent !== true){
24755 this.fireEvent('check', this, true);
24763 validate : function()
24765 if(this.getVisibilityEl().hasClass('hidden')){
24771 (this.inputType == 'radio' && this.validateRadio()) ||
24772 (this.inputType == 'checkbox' && this.validateCheckbox())
24778 this.markInvalid();
24782 validateRadio : function()
24784 if(this.getVisibilityEl().hasClass('hidden')){
24788 if(this.allowBlank){
24794 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24795 if(!e.dom.checked){
24807 validateCheckbox : function()
24810 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24811 //return (this.getValue() == this.inputValue) ? true : false;
24814 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24822 for(var i in group){
24823 if(group[i].el.isVisible(true)){
24831 for(var i in group){
24836 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24843 * Mark this field as valid
24845 markValid : function()
24849 this.fireEvent('valid', this);
24851 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24854 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24861 if(this.inputType == 'radio'){
24862 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24863 var fg = e.findParent('.form-group', false, true);
24864 if (Roo.bootstrap.version == 3) {
24865 fg.removeClass([_this.invalidClass, _this.validClass]);
24866 fg.addClass(_this.validClass);
24868 fg.removeClass(['is-valid', 'is-invalid']);
24869 fg.addClass('is-valid');
24877 var fg = this.el.findParent('.form-group', false, true);
24878 if (Roo.bootstrap.version == 3) {
24879 fg.removeClass([this.invalidClass, this.validClass]);
24880 fg.addClass(this.validClass);
24882 fg.removeClass(['is-valid', 'is-invalid']);
24883 fg.addClass('is-valid');
24888 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24894 for(var i in group){
24895 var fg = group[i].el.findParent('.form-group', false, true);
24896 if (Roo.bootstrap.version == 3) {
24897 fg.removeClass([this.invalidClass, this.validClass]);
24898 fg.addClass(this.validClass);
24900 fg.removeClass(['is-valid', 'is-invalid']);
24901 fg.addClass('is-valid');
24907 * Mark this field as invalid
24908 * @param {String} msg The validation message
24910 markInvalid : function(msg)
24912 if(this.allowBlank){
24918 this.fireEvent('invalid', this, msg);
24920 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24923 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24927 label.markInvalid();
24930 if(this.inputType == 'radio'){
24932 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24933 var fg = e.findParent('.form-group', false, true);
24934 if (Roo.bootstrap.version == 3) {
24935 fg.removeClass([_this.invalidClass, _this.validClass]);
24936 fg.addClass(_this.invalidClass);
24938 fg.removeClass(['is-invalid', 'is-valid']);
24939 fg.addClass('is-invalid');
24947 var fg = this.el.findParent('.form-group', false, true);
24948 if (Roo.bootstrap.version == 3) {
24949 fg.removeClass([_this.invalidClass, _this.validClass]);
24950 fg.addClass(_this.invalidClass);
24952 fg.removeClass(['is-invalid', 'is-valid']);
24953 fg.addClass('is-invalid');
24958 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24964 for(var i in group){
24965 var fg = group[i].el.findParent('.form-group', false, true);
24966 if (Roo.bootstrap.version == 3) {
24967 fg.removeClass([_this.invalidClass, _this.validClass]);
24968 fg.addClass(_this.invalidClass);
24970 fg.removeClass(['is-invalid', 'is-valid']);
24971 fg.addClass('is-invalid');
24977 clearInvalid : function()
24979 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24981 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24983 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24985 if (label && label.iconEl) {
24986 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24987 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24991 disable : function()
24993 if(this.inputType != 'radio'){
24994 Roo.bootstrap.CheckBox.superclass.disable.call(this);
25001 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25002 _this.getActionEl().addClass(this.disabledClass);
25003 e.dom.disabled = true;
25007 this.disabled = true;
25008 this.fireEvent("disable", this);
25012 enable : function()
25014 if(this.inputType != 'radio'){
25015 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25022 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25023 _this.getActionEl().removeClass(this.disabledClass);
25024 e.dom.disabled = false;
25028 this.disabled = false;
25029 this.fireEvent("enable", this);
25033 setBoxLabel : function(v)
25038 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25044 Roo.apply(Roo.bootstrap.CheckBox, {
25049 * register a CheckBox Group
25050 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25052 register : function(checkbox)
25054 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25055 this.groups[checkbox.groupId] = {};
25058 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25062 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25066 * fetch a CheckBox Group based on the group ID
25067 * @param {string} the group ID
25068 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25070 get: function(groupId) {
25071 if (typeof(this.groups[groupId]) == 'undefined') {
25075 return this.groups[groupId] ;
25088 * @class Roo.bootstrap.Radio
25089 * @extends Roo.bootstrap.Component
25090 * Bootstrap Radio class
25091 * @cfg {String} boxLabel - the label associated
25092 * @cfg {String} value - the value of radio
25095 * Create a new Radio
25096 * @param {Object} config The config object
25098 Roo.bootstrap.Radio = function(config){
25099 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25103 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25109 getAutoCreate : function()
25113 cls : 'form-group radio',
25118 html : this.boxLabel
25126 initEvents : function()
25128 this.parent().register(this);
25130 this.el.on('click', this.onClick, this);
25134 onClick : function(e)
25136 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25137 this.setChecked(true);
25141 setChecked : function(state, suppressEvent)
25143 this.parent().setValue(this.value, suppressEvent);
25147 setBoxLabel : function(v)
25152 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25167 * @class Roo.bootstrap.SecurePass
25168 * @extends Roo.bootstrap.Input
25169 * Bootstrap SecurePass class
25173 * Create a new SecurePass
25174 * @param {Object} config The config object
25177 Roo.bootstrap.SecurePass = function (config) {
25178 // these go here, so the translation tool can replace them..
25180 PwdEmpty: "Please type a password, and then retype it to confirm.",
25181 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25182 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25183 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25184 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25185 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25186 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25187 TooWeak: "Your password is Too Weak."
25189 this.meterLabel = "Password strength:";
25190 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25191 this.meterClass = [
25192 "roo-password-meter-tooweak",
25193 "roo-password-meter-weak",
25194 "roo-password-meter-medium",
25195 "roo-password-meter-strong",
25196 "roo-password-meter-grey"
25201 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25204 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25206 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25208 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25209 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25210 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25211 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25212 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25213 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25214 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25224 * @cfg {String/Object} Label for the strength meter (defaults to
25225 * 'Password strength:')
25230 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25231 * ['Weak', 'Medium', 'Strong'])
25234 pwdStrengths: false,
25247 initEvents: function ()
25249 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25251 if (this.el.is('input[type=password]') && Roo.isSafari) {
25252 this.el.on('keydown', this.SafariOnKeyDown, this);
25255 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25258 onRender: function (ct, position)
25260 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25261 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25262 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25264 this.trigger.createChild({
25269 cls: 'roo-password-meter-grey col-xs-12',
25272 //width: this.meterWidth + 'px'
25276 cls: 'roo-password-meter-text'
25282 if (this.hideTrigger) {
25283 this.trigger.setDisplayed(false);
25285 this.setSize(this.width || '', this.height || '');
25288 onDestroy: function ()
25290 if (this.trigger) {
25291 this.trigger.removeAllListeners();
25292 this.trigger.remove();
25295 this.wrap.remove();
25297 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25300 checkStrength: function ()
25302 var pwd = this.inputEl().getValue();
25303 if (pwd == this._lastPwd) {
25308 if (this.ClientSideStrongPassword(pwd)) {
25310 } else if (this.ClientSideMediumPassword(pwd)) {
25312 } else if (this.ClientSideWeakPassword(pwd)) {
25318 Roo.log('strength1: ' + strength);
25320 //var pm = this.trigger.child('div/div/div').dom;
25321 var pm = this.trigger.child('div/div');
25322 pm.removeClass(this.meterClass);
25323 pm.addClass(this.meterClass[strength]);
25326 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25328 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25330 this._lastPwd = pwd;
25334 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25336 this._lastPwd = '';
25338 var pm = this.trigger.child('div/div');
25339 pm.removeClass(this.meterClass);
25340 pm.addClass('roo-password-meter-grey');
25343 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25346 this.inputEl().dom.type='password';
25349 validateValue: function (value)
25351 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25354 if (value.length == 0) {
25355 if (this.allowBlank) {
25356 this.clearInvalid();
25360 this.markInvalid(this.errors.PwdEmpty);
25361 this.errorMsg = this.errors.PwdEmpty;
25369 if (!value.match(/[\x21-\x7e]+/)) {
25370 this.markInvalid(this.errors.PwdBadChar);
25371 this.errorMsg = this.errors.PwdBadChar;
25374 if (value.length < 6) {
25375 this.markInvalid(this.errors.PwdShort);
25376 this.errorMsg = this.errors.PwdShort;
25379 if (value.length > 16) {
25380 this.markInvalid(this.errors.PwdLong);
25381 this.errorMsg = this.errors.PwdLong;
25385 if (this.ClientSideStrongPassword(value)) {
25387 } else if (this.ClientSideMediumPassword(value)) {
25389 } else if (this.ClientSideWeakPassword(value)) {
25396 if (strength < 2) {
25397 //this.markInvalid(this.errors.TooWeak);
25398 this.errorMsg = this.errors.TooWeak;
25403 console.log('strength2: ' + strength);
25405 //var pm = this.trigger.child('div/div/div').dom;
25407 var pm = this.trigger.child('div/div');
25408 pm.removeClass(this.meterClass);
25409 pm.addClass(this.meterClass[strength]);
25411 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25413 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25415 this.errorMsg = '';
25419 CharacterSetChecks: function (type)
25422 this.fResult = false;
25425 isctype: function (character, type)
25428 case this.kCapitalLetter:
25429 if (character >= 'A' && character <= 'Z') {
25434 case this.kSmallLetter:
25435 if (character >= 'a' && character <= 'z') {
25441 if (character >= '0' && character <= '9') {
25446 case this.kPunctuation:
25447 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25458 IsLongEnough: function (pwd, size)
25460 return !(pwd == null || isNaN(size) || pwd.length < size);
25463 SpansEnoughCharacterSets: function (word, nb)
25465 if (!this.IsLongEnough(word, nb))
25470 var characterSetChecks = new Array(
25471 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25472 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25475 for (var index = 0; index < word.length; ++index) {
25476 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25477 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25478 characterSetChecks[nCharSet].fResult = true;
25485 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25486 if (characterSetChecks[nCharSet].fResult) {
25491 if (nCharSets < nb) {
25497 ClientSideStrongPassword: function (pwd)
25499 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25502 ClientSideMediumPassword: function (pwd)
25504 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25507 ClientSideWeakPassword: function (pwd)
25509 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25512 })//<script type="text/javascript">
25515 * Based Ext JS Library 1.1.1
25516 * Copyright(c) 2006-2007, Ext JS, LLC.
25522 * @class Roo.HtmlEditorCore
25523 * @extends Roo.Component
25524 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25526 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25529 Roo.HtmlEditorCore = function(config){
25532 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25537 * @event initialize
25538 * Fires when the editor is fully initialized (including the iframe)
25539 * @param {Roo.HtmlEditorCore} this
25544 * Fires when the editor is first receives the focus. Any insertion must wait
25545 * until after this event.
25546 * @param {Roo.HtmlEditorCore} this
25550 * @event beforesync
25551 * Fires before the textarea is updated with content from the editor iframe. Return false
25552 * to cancel the sync.
25553 * @param {Roo.HtmlEditorCore} this
25554 * @param {String} html
25558 * @event beforepush
25559 * Fires before the iframe editor is updated with content from the textarea. Return false
25560 * to cancel the push.
25561 * @param {Roo.HtmlEditorCore} this
25562 * @param {String} html
25567 * Fires when the textarea is updated with content from the editor iframe.
25568 * @param {Roo.HtmlEditorCore} this
25569 * @param {String} html
25574 * Fires when the iframe editor is updated with content from the textarea.
25575 * @param {Roo.HtmlEditorCore} this
25576 * @param {String} html
25581 * @event editorevent
25582 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25583 * @param {Roo.HtmlEditorCore} this
25589 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25591 // defaults : white / black...
25592 this.applyBlacklists();
25599 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25603 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25609 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25614 * @cfg {Number} height (in pixels)
25618 * @cfg {Number} width (in pixels)
25623 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25626 stylesheets: false,
25629 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25631 allowComments: false,
25635 // private properties
25636 validationEvent : false,
25638 initialized : false,
25640 sourceEditMode : false,
25641 onFocus : Roo.emptyFn,
25643 hideMode:'offsets',
25647 // blacklist + whitelisted elements..
25654 * Protected method that will not generally be called directly. It
25655 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25656 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25658 getDocMarkup : function(){
25662 // inherit styels from page...??
25663 if (this.stylesheets === false) {
25665 Roo.get(document.head).select('style').each(function(node) {
25666 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25669 Roo.get(document.head).select('link').each(function(node) {
25670 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25673 } else if (!this.stylesheets.length) {
25675 st = '<style type="text/css">' +
25676 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25679 for (var i in this.stylesheets) {
25680 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25685 st += '<style type="text/css">' +
25686 'IMG { cursor: pointer } ' +
25689 var cls = 'roo-htmleditor-body';
25691 if(this.bodyCls.length){
25692 cls += ' ' + this.bodyCls;
25695 return '<html><head>' + st +
25696 //<style type="text/css">' +
25697 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25699 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25703 onRender : function(ct, position)
25706 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25707 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25710 this.el.dom.style.border = '0 none';
25711 this.el.dom.setAttribute('tabIndex', -1);
25712 this.el.addClass('x-hidden hide');
25716 if(Roo.isIE){ // fix IE 1px bogus margin
25717 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25721 this.frameId = Roo.id();
25725 var iframe = this.owner.wrap.createChild({
25727 cls: 'form-control', // bootstrap..
25729 name: this.frameId,
25730 frameBorder : 'no',
25731 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25736 this.iframe = iframe.dom;
25738 this.assignDocWin();
25740 this.doc.designMode = 'on';
25743 this.doc.write(this.getDocMarkup());
25747 var task = { // must defer to wait for browser to be ready
25749 //console.log("run task?" + this.doc.readyState);
25750 this.assignDocWin();
25751 if(this.doc.body || this.doc.readyState == 'complete'){
25753 this.doc.designMode="on";
25757 Roo.TaskMgr.stop(task);
25758 this.initEditor.defer(10, this);
25765 Roo.TaskMgr.start(task);
25770 onResize : function(w, h)
25772 Roo.log('resize: ' +w + ',' + h );
25773 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25777 if(typeof w == 'number'){
25779 this.iframe.style.width = w + 'px';
25781 if(typeof h == 'number'){
25783 this.iframe.style.height = h + 'px';
25785 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25792 * Toggles the editor between standard and source edit mode.
25793 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25795 toggleSourceEdit : function(sourceEditMode){
25797 this.sourceEditMode = sourceEditMode === true;
25799 if(this.sourceEditMode){
25801 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25804 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25805 //this.iframe.className = '';
25808 //this.setSize(this.owner.wrap.getSize());
25809 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25816 * Protected method that will not generally be called directly. If you need/want
25817 * custom HTML cleanup, this is the method you should override.
25818 * @param {String} html The HTML to be cleaned
25819 * return {String} The cleaned HTML
25821 cleanHtml : function(html){
25822 html = String(html);
25823 if(html.length > 5){
25824 if(Roo.isSafari){ // strip safari nonsense
25825 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25828 if(html == ' '){
25835 * HTML Editor -> Textarea
25836 * Protected method that will not generally be called directly. Syncs the contents
25837 * of the editor iframe with the textarea.
25839 syncValue : function(){
25840 if(this.initialized){
25841 var bd = (this.doc.body || this.doc.documentElement);
25842 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25843 var html = bd.innerHTML;
25845 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25846 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25848 html = '<div style="'+m[0]+'">' + html + '</div>';
25851 html = this.cleanHtml(html);
25852 // fix up the special chars.. normaly like back quotes in word...
25853 // however we do not want to do this with chinese..
25854 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25856 var cc = match.charCodeAt();
25858 // Get the character value, handling surrogate pairs
25859 if (match.length == 2) {
25860 // It's a surrogate pair, calculate the Unicode code point
25861 var high = match.charCodeAt(0) - 0xD800;
25862 var low = match.charCodeAt(1) - 0xDC00;
25863 cc = (high * 0x400) + low + 0x10000;
25865 (cc >= 0x4E00 && cc < 0xA000 ) ||
25866 (cc >= 0x3400 && cc < 0x4E00 ) ||
25867 (cc >= 0xf900 && cc < 0xfb00 )
25872 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25873 return "&#" + cc + ";";
25880 if(this.owner.fireEvent('beforesync', this, html) !== false){
25881 this.el.dom.value = html;
25882 this.owner.fireEvent('sync', this, html);
25888 * Protected method that will not generally be called directly. Pushes the value of the textarea
25889 * into the iframe editor.
25891 pushValue : function(){
25892 if(this.initialized){
25893 var v = this.el.dom.value.trim();
25895 // if(v.length < 1){
25899 if(this.owner.fireEvent('beforepush', this, v) !== false){
25900 var d = (this.doc.body || this.doc.documentElement);
25902 this.cleanUpPaste();
25903 this.el.dom.value = d.innerHTML;
25904 this.owner.fireEvent('push', this, v);
25910 deferFocus : function(){
25911 this.focus.defer(10, this);
25915 focus : function(){
25916 if(this.win && !this.sourceEditMode){
25923 assignDocWin: function()
25925 var iframe = this.iframe;
25928 this.doc = iframe.contentWindow.document;
25929 this.win = iframe.contentWindow;
25931 // if (!Roo.get(this.frameId)) {
25934 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25935 // this.win = Roo.get(this.frameId).dom.contentWindow;
25937 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25941 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25942 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25947 initEditor : function(){
25948 //console.log("INIT EDITOR");
25949 this.assignDocWin();
25953 this.doc.designMode="on";
25955 this.doc.write(this.getDocMarkup());
25958 var dbody = (this.doc.body || this.doc.documentElement);
25959 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25960 // this copies styles from the containing element into thsi one..
25961 // not sure why we need all of this..
25962 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25964 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25965 //ss['background-attachment'] = 'fixed'; // w3c
25966 dbody.bgProperties = 'fixed'; // ie
25967 //Roo.DomHelper.applyStyles(dbody, ss);
25968 Roo.EventManager.on(this.doc, {
25969 //'mousedown': this.onEditorEvent,
25970 'mouseup': this.onEditorEvent,
25971 'dblclick': this.onEditorEvent,
25972 'click': this.onEditorEvent,
25973 'keyup': this.onEditorEvent,
25978 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25980 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25981 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25983 this.initialized = true;
25985 this.owner.fireEvent('initialize', this);
25990 onDestroy : function(){
25996 //for (var i =0; i < this.toolbars.length;i++) {
25997 // // fixme - ask toolbars for heights?
25998 // this.toolbars[i].onDestroy();
26001 //this.wrap.dom.innerHTML = '';
26002 //this.wrap.remove();
26007 onFirstFocus : function(){
26009 this.assignDocWin();
26012 this.activated = true;
26015 if(Roo.isGecko){ // prevent silly gecko errors
26017 var s = this.win.getSelection();
26018 if(!s.focusNode || s.focusNode.nodeType != 3){
26019 var r = s.getRangeAt(0);
26020 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26025 this.execCmd('useCSS', true);
26026 this.execCmd('styleWithCSS', false);
26029 this.owner.fireEvent('activate', this);
26033 adjustFont: function(btn){
26034 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26035 //if(Roo.isSafari){ // safari
26038 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26039 if(Roo.isSafari){ // safari
26040 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26041 v = (v < 10) ? 10 : v;
26042 v = (v > 48) ? 48 : v;
26043 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26048 v = Math.max(1, v+adjust);
26050 this.execCmd('FontSize', v );
26053 onEditorEvent : function(e)
26055 this.owner.fireEvent('editorevent', this, e);
26056 // this.updateToolbar();
26057 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26060 insertTag : function(tg)
26062 // could be a bit smarter... -> wrap the current selected tRoo..
26063 if (tg.toLowerCase() == 'span' ||
26064 tg.toLowerCase() == 'code' ||
26065 tg.toLowerCase() == 'sup' ||
26066 tg.toLowerCase() == 'sub'
26069 range = this.createRange(this.getSelection());
26070 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26071 wrappingNode.appendChild(range.extractContents());
26072 range.insertNode(wrappingNode);
26079 this.execCmd("formatblock", tg);
26083 insertText : function(txt)
26087 var range = this.createRange();
26088 range.deleteContents();
26089 //alert(Sender.getAttribute('label'));
26091 range.insertNode(this.doc.createTextNode(txt));
26097 * Executes a Midas editor command on the editor document and performs necessary focus and
26098 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26099 * @param {String} cmd The Midas command
26100 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26102 relayCmd : function(cmd, value){
26104 this.execCmd(cmd, value);
26105 this.owner.fireEvent('editorevent', this);
26106 //this.updateToolbar();
26107 this.owner.deferFocus();
26111 * Executes a Midas editor command directly on the editor document.
26112 * For visual commands, you should use {@link #relayCmd} instead.
26113 * <b>This should only be called after the editor is initialized.</b>
26114 * @param {String} cmd The Midas command
26115 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26117 execCmd : function(cmd, value){
26118 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26125 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26127 * @param {String} text | dom node..
26129 insertAtCursor : function(text)
26132 if(!this.activated){
26138 var r = this.doc.selection.createRange();
26149 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26153 // from jquery ui (MIT licenced)
26155 var win = this.win;
26157 if (win.getSelection && win.getSelection().getRangeAt) {
26158 range = win.getSelection().getRangeAt(0);
26159 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26160 range.insertNode(node);
26161 } else if (win.document.selection && win.document.selection.createRange) {
26162 // no firefox support
26163 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26164 win.document.selection.createRange().pasteHTML(txt);
26166 // no firefox support
26167 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26168 this.execCmd('InsertHTML', txt);
26177 mozKeyPress : function(e){
26179 var c = e.getCharCode(), cmd;
26182 c = String.fromCharCode(c).toLowerCase();
26196 this.cleanUpPaste.defer(100, this);
26204 e.preventDefault();
26212 fixKeys : function(){ // load time branching for fastest keydown performance
26214 return function(e){
26215 var k = e.getKey(), r;
26218 r = this.doc.selection.createRange();
26221 r.pasteHTML('    ');
26228 r = this.doc.selection.createRange();
26230 var target = r.parentElement();
26231 if(!target || target.tagName.toLowerCase() != 'li'){
26233 r.pasteHTML('<br />');
26239 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26240 this.cleanUpPaste.defer(100, this);
26246 }else if(Roo.isOpera){
26247 return function(e){
26248 var k = e.getKey();
26252 this.execCmd('InsertHTML','    ');
26255 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26256 this.cleanUpPaste.defer(100, this);
26261 }else if(Roo.isSafari){
26262 return function(e){
26263 var k = e.getKey();
26267 this.execCmd('InsertText','\t');
26271 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26272 this.cleanUpPaste.defer(100, this);
26280 getAllAncestors: function()
26282 var p = this.getSelectedNode();
26285 a.push(p); // push blank onto stack..
26286 p = this.getParentElement();
26290 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26294 a.push(this.doc.body);
26298 lastSelNode : false,
26301 getSelection : function()
26303 this.assignDocWin();
26304 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26307 getSelectedNode: function()
26309 // this may only work on Gecko!!!
26311 // should we cache this!!!!
26316 var range = this.createRange(this.getSelection()).cloneRange();
26319 var parent = range.parentElement();
26321 var testRange = range.duplicate();
26322 testRange.moveToElementText(parent);
26323 if (testRange.inRange(range)) {
26326 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26329 parent = parent.parentElement;
26334 // is ancestor a text element.
26335 var ac = range.commonAncestorContainer;
26336 if (ac.nodeType == 3) {
26337 ac = ac.parentNode;
26340 var ar = ac.childNodes;
26343 var other_nodes = [];
26344 var has_other_nodes = false;
26345 for (var i=0;i<ar.length;i++) {
26346 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26349 // fullly contained node.
26351 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26356 // probably selected..
26357 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26358 other_nodes.push(ar[i]);
26362 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26367 has_other_nodes = true;
26369 if (!nodes.length && other_nodes.length) {
26370 nodes= other_nodes;
26372 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26378 createRange: function(sel)
26380 // this has strange effects when using with
26381 // top toolbar - not sure if it's a great idea.
26382 //this.editor.contentWindow.focus();
26383 if (typeof sel != "undefined") {
26385 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26387 return this.doc.createRange();
26390 return this.doc.createRange();
26393 getParentElement: function()
26396 this.assignDocWin();
26397 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26399 var range = this.createRange(sel);
26402 var p = range.commonAncestorContainer;
26403 while (p.nodeType == 3) { // text node
26414 * Range intersection.. the hard stuff...
26418 * [ -- selected range --- ]
26422 * if end is before start or hits it. fail.
26423 * if start is after end or hits it fail.
26425 * if either hits (but other is outside. - then it's not
26431 // @see http://www.thismuchiknow.co.uk/?p=64.
26432 rangeIntersectsNode : function(range, node)
26434 var nodeRange = node.ownerDocument.createRange();
26436 nodeRange.selectNode(node);
26438 nodeRange.selectNodeContents(node);
26441 var rangeStartRange = range.cloneRange();
26442 rangeStartRange.collapse(true);
26444 var rangeEndRange = range.cloneRange();
26445 rangeEndRange.collapse(false);
26447 var nodeStartRange = nodeRange.cloneRange();
26448 nodeStartRange.collapse(true);
26450 var nodeEndRange = nodeRange.cloneRange();
26451 nodeEndRange.collapse(false);
26453 return rangeStartRange.compareBoundaryPoints(
26454 Range.START_TO_START, nodeEndRange) == -1 &&
26455 rangeEndRange.compareBoundaryPoints(
26456 Range.START_TO_START, nodeStartRange) == 1;
26460 rangeCompareNode : function(range, node)
26462 var nodeRange = node.ownerDocument.createRange();
26464 nodeRange.selectNode(node);
26466 nodeRange.selectNodeContents(node);
26470 range.collapse(true);
26472 nodeRange.collapse(true);
26474 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26475 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26477 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26479 var nodeIsBefore = ss == 1;
26480 var nodeIsAfter = ee == -1;
26482 if (nodeIsBefore && nodeIsAfter) {
26485 if (!nodeIsBefore && nodeIsAfter) {
26486 return 1; //right trailed.
26489 if (nodeIsBefore && !nodeIsAfter) {
26490 return 2; // left trailed.
26496 // private? - in a new class?
26497 cleanUpPaste : function()
26499 // cleans up the whole document..
26500 Roo.log('cleanuppaste');
26502 this.cleanUpChildren(this.doc.body);
26503 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26504 if (clean != this.doc.body.innerHTML) {
26505 this.doc.body.innerHTML = clean;
26510 cleanWordChars : function(input) {// change the chars to hex code
26511 var he = Roo.HtmlEditorCore;
26513 var output = input;
26514 Roo.each(he.swapCodes, function(sw) {
26515 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26517 output = output.replace(swapper, sw[1]);
26524 cleanUpChildren : function (n)
26526 if (!n.childNodes.length) {
26529 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26530 this.cleanUpChild(n.childNodes[i]);
26537 cleanUpChild : function (node)
26540 //console.log(node);
26541 if (node.nodeName == "#text") {
26542 // clean up silly Windows -- stuff?
26545 if (node.nodeName == "#comment") {
26546 if (!this.allowComments) {
26547 node.parentNode.removeChild(node);
26549 // clean up silly Windows -- stuff?
26552 var lcname = node.tagName.toLowerCase();
26553 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26554 // whitelist of tags..
26556 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26558 node.parentNode.removeChild(node);
26563 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26565 // spans with no attributes - just remove them..
26566 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26567 remove_keep_children = true;
26570 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26571 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26573 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26574 // remove_keep_children = true;
26577 if (remove_keep_children) {
26578 this.cleanUpChildren(node);
26579 // inserts everything just before this node...
26580 while (node.childNodes.length) {
26581 var cn = node.childNodes[0];
26582 node.removeChild(cn);
26583 node.parentNode.insertBefore(cn, node);
26585 node.parentNode.removeChild(node);
26589 if (!node.attributes || !node.attributes.length) {
26594 this.cleanUpChildren(node);
26598 function cleanAttr(n,v)
26601 if (v.match(/^\./) || v.match(/^\//)) {
26604 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26607 if (v.match(/^#/)) {
26610 if (v.match(/^\{/)) { // allow template editing.
26613 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26614 node.removeAttribute(n);
26618 var cwhite = this.cwhite;
26619 var cblack = this.cblack;
26621 function cleanStyle(n,v)
26623 if (v.match(/expression/)) { //XSS?? should we even bother..
26624 node.removeAttribute(n);
26628 var parts = v.split(/;/);
26631 Roo.each(parts, function(p) {
26632 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26636 var l = p.split(':').shift().replace(/\s+/g,'');
26637 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26639 if ( cwhite.length && cblack.indexOf(l) > -1) {
26640 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26641 //node.removeAttribute(n);
26645 // only allow 'c whitelisted system attributes'
26646 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26647 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26648 //node.removeAttribute(n);
26658 if (clean.length) {
26659 node.setAttribute(n, clean.join(';'));
26661 node.removeAttribute(n);
26667 for (var i = node.attributes.length-1; i > -1 ; i--) {
26668 var a = node.attributes[i];
26671 if (a.name.toLowerCase().substr(0,2)=='on') {
26672 node.removeAttribute(a.name);
26675 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26676 node.removeAttribute(a.name);
26679 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26680 cleanAttr(a.name,a.value); // fixme..
26683 if (a.name == 'style') {
26684 cleanStyle(a.name,a.value);
26687 /// clean up MS crap..
26688 // tecnically this should be a list of valid class'es..
26691 if (a.name == 'class') {
26692 if (a.value.match(/^Mso/)) {
26693 node.removeAttribute('class');
26696 if (a.value.match(/^body$/)) {
26697 node.removeAttribute('class');
26708 this.cleanUpChildren(node);
26714 * Clean up MS wordisms...
26716 cleanWord : function(node)
26719 this.cleanWord(this.doc.body);
26724 node.nodeName == 'SPAN' &&
26725 !node.hasAttributes() &&
26726 node.childNodes.length == 1 &&
26727 node.firstChild.nodeName == "#text"
26729 var textNode = node.firstChild;
26730 node.removeChild(textNode);
26731 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26732 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26734 node.parentNode.insertBefore(textNode, node);
26735 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26736 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26738 node.parentNode.removeChild(node);
26741 if (node.nodeName == "#text") {
26742 // clean up silly Windows -- stuff?
26745 if (node.nodeName == "#comment") {
26746 node.parentNode.removeChild(node);
26747 // clean up silly Windows -- stuff?
26751 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26752 node.parentNode.removeChild(node);
26755 //Roo.log(node.tagName);
26756 // remove - but keep children..
26757 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26758 //Roo.log('-- removed');
26759 while (node.childNodes.length) {
26760 var cn = node.childNodes[0];
26761 node.removeChild(cn);
26762 node.parentNode.insertBefore(cn, node);
26763 // move node to parent - and clean it..
26764 this.cleanWord(cn);
26766 node.parentNode.removeChild(node);
26767 /// no need to iterate chidlren = it's got none..
26768 //this.iterateChildren(node, this.cleanWord);
26772 if (node.className.length) {
26774 var cn = node.className.split(/\W+/);
26776 Roo.each(cn, function(cls) {
26777 if (cls.match(/Mso[a-zA-Z]+/)) {
26782 node.className = cna.length ? cna.join(' ') : '';
26784 node.removeAttribute("class");
26788 if (node.hasAttribute("lang")) {
26789 node.removeAttribute("lang");
26792 if (node.hasAttribute("style")) {
26794 var styles = node.getAttribute("style").split(";");
26796 Roo.each(styles, function(s) {
26797 if (!s.match(/:/)) {
26800 var kv = s.split(":");
26801 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26804 // what ever is left... we allow.
26807 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26808 if (!nstyle.length) {
26809 node.removeAttribute('style');
26812 this.iterateChildren(node, this.cleanWord);
26818 * iterateChildren of a Node, calling fn each time, using this as the scole..
26819 * @param {DomNode} node node to iterate children of.
26820 * @param {Function} fn method of this class to call on each item.
26822 iterateChildren : function(node, fn)
26824 if (!node.childNodes.length) {
26827 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26828 fn.call(this, node.childNodes[i])
26834 * cleanTableWidths.
26836 * Quite often pasting from word etc.. results in tables with column and widths.
26837 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26840 cleanTableWidths : function(node)
26845 this.cleanTableWidths(this.doc.body);
26850 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26853 Roo.log(node.tagName);
26854 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26855 this.iterateChildren(node, this.cleanTableWidths);
26858 if (node.hasAttribute('width')) {
26859 node.removeAttribute('width');
26863 if (node.hasAttribute("style")) {
26866 var styles = node.getAttribute("style").split(";");
26868 Roo.each(styles, function(s) {
26869 if (!s.match(/:/)) {
26872 var kv = s.split(":");
26873 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26876 // what ever is left... we allow.
26879 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26880 if (!nstyle.length) {
26881 node.removeAttribute('style');
26885 this.iterateChildren(node, this.cleanTableWidths);
26893 domToHTML : function(currentElement, depth, nopadtext) {
26895 depth = depth || 0;
26896 nopadtext = nopadtext || false;
26898 if (!currentElement) {
26899 return this.domToHTML(this.doc.body);
26902 //Roo.log(currentElement);
26904 var allText = false;
26905 var nodeName = currentElement.nodeName;
26906 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26908 if (nodeName == '#text') {
26910 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26915 if (nodeName != 'BODY') {
26918 // Prints the node tagName, such as <A>, <IMG>, etc
26921 for(i = 0; i < currentElement.attributes.length;i++) {
26923 var aname = currentElement.attributes.item(i).name;
26924 if (!currentElement.attributes.item(i).value.length) {
26927 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26930 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26939 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26942 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26947 // Traverse the tree
26949 var currentElementChild = currentElement.childNodes.item(i);
26950 var allText = true;
26951 var innerHTML = '';
26953 while (currentElementChild) {
26954 // Formatting code (indent the tree so it looks nice on the screen)
26955 var nopad = nopadtext;
26956 if (lastnode == 'SPAN') {
26960 if (currentElementChild.nodeName == '#text') {
26961 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26962 toadd = nopadtext ? toadd : toadd.trim();
26963 if (!nopad && toadd.length > 80) {
26964 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26966 innerHTML += toadd;
26969 currentElementChild = currentElement.childNodes.item(i);
26975 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26977 // Recursively traverse the tree structure of the child node
26978 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26979 lastnode = currentElementChild.nodeName;
26981 currentElementChild=currentElement.childNodes.item(i);
26987 // The remaining code is mostly for formatting the tree
26988 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26993 ret+= "</"+tagName+">";
26999 applyBlacklists : function()
27001 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27002 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27006 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27007 if (b.indexOf(tag) > -1) {
27010 this.white.push(tag);
27014 Roo.each(w, function(tag) {
27015 if (b.indexOf(tag) > -1) {
27018 if (this.white.indexOf(tag) > -1) {
27021 this.white.push(tag);
27026 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27027 if (w.indexOf(tag) > -1) {
27030 this.black.push(tag);
27034 Roo.each(b, function(tag) {
27035 if (w.indexOf(tag) > -1) {
27038 if (this.black.indexOf(tag) > -1) {
27041 this.black.push(tag);
27046 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27047 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27051 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27052 if (b.indexOf(tag) > -1) {
27055 this.cwhite.push(tag);
27059 Roo.each(w, function(tag) {
27060 if (b.indexOf(tag) > -1) {
27063 if (this.cwhite.indexOf(tag) > -1) {
27066 this.cwhite.push(tag);
27071 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27072 if (w.indexOf(tag) > -1) {
27075 this.cblack.push(tag);
27079 Roo.each(b, function(tag) {
27080 if (w.indexOf(tag) > -1) {
27083 if (this.cblack.indexOf(tag) > -1) {
27086 this.cblack.push(tag);
27091 setStylesheets : function(stylesheets)
27093 if(typeof(stylesheets) == 'string'){
27094 Roo.get(this.iframe.contentDocument.head).createChild({
27096 rel : 'stylesheet',
27105 Roo.each(stylesheets, function(s) {
27110 Roo.get(_this.iframe.contentDocument.head).createChild({
27112 rel : 'stylesheet',
27121 removeStylesheets : function()
27125 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27130 setStyle : function(style)
27132 Roo.get(this.iframe.contentDocument.head).createChild({
27141 // hide stuff that is not compatible
27155 * @event specialkey
27159 * @cfg {String} fieldClass @hide
27162 * @cfg {String} focusClass @hide
27165 * @cfg {String} autoCreate @hide
27168 * @cfg {String} inputType @hide
27171 * @cfg {String} invalidClass @hide
27174 * @cfg {String} invalidText @hide
27177 * @cfg {String} msgFx @hide
27180 * @cfg {String} validateOnBlur @hide
27184 Roo.HtmlEditorCore.white = [
27185 'area', 'br', 'img', 'input', 'hr', 'wbr',
27187 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27188 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27189 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27190 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27191 'table', 'ul', 'xmp',
27193 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27196 'dir', 'menu', 'ol', 'ul', 'dl',
27202 Roo.HtmlEditorCore.black = [
27203 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27205 'base', 'basefont', 'bgsound', 'blink', 'body',
27206 'frame', 'frameset', 'head', 'html', 'ilayer',
27207 'iframe', 'layer', 'link', 'meta', 'object',
27208 'script', 'style' ,'title', 'xml' // clean later..
27210 Roo.HtmlEditorCore.clean = [
27211 'script', 'style', 'title', 'xml'
27213 Roo.HtmlEditorCore.remove = [
27218 Roo.HtmlEditorCore.ablack = [
27222 Roo.HtmlEditorCore.aclean = [
27223 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27227 Roo.HtmlEditorCore.pwhite= [
27228 'http', 'https', 'mailto'
27231 // white listed style attributes.
27232 Roo.HtmlEditorCore.cwhite= [
27233 // 'text-align', /// default is to allow most things..
27239 // black listed style attributes.
27240 Roo.HtmlEditorCore.cblack= [
27241 // 'font-size' -- this can be set by the project
27245 Roo.HtmlEditorCore.swapCodes =[
27246 [ 8211, "–" ],
27247 [ 8212, "—" ],
27264 * @class Roo.bootstrap.HtmlEditor
27265 * @extends Roo.bootstrap.TextArea
27266 * Bootstrap HtmlEditor class
27269 * Create a new HtmlEditor
27270 * @param {Object} config The config object
27273 Roo.bootstrap.HtmlEditor = function(config){
27274 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27275 if (!this.toolbars) {
27276 this.toolbars = [];
27279 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27282 * @event initialize
27283 * Fires when the editor is fully initialized (including the iframe)
27284 * @param {HtmlEditor} this
27289 * Fires when the editor is first receives the focus. Any insertion must wait
27290 * until after this event.
27291 * @param {HtmlEditor} this
27295 * @event beforesync
27296 * Fires before the textarea is updated with content from the editor iframe. Return false
27297 * to cancel the sync.
27298 * @param {HtmlEditor} this
27299 * @param {String} html
27303 * @event beforepush
27304 * Fires before the iframe editor is updated with content from the textarea. Return false
27305 * to cancel the push.
27306 * @param {HtmlEditor} this
27307 * @param {String} html
27312 * Fires when the textarea is updated with content from the editor iframe.
27313 * @param {HtmlEditor} this
27314 * @param {String} html
27319 * Fires when the iframe editor is updated with content from the textarea.
27320 * @param {HtmlEditor} this
27321 * @param {String} html
27325 * @event editmodechange
27326 * Fires when the editor switches edit modes
27327 * @param {HtmlEditor} this
27328 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27330 editmodechange: true,
27332 * @event editorevent
27333 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27334 * @param {HtmlEditor} this
27338 * @event firstfocus
27339 * Fires when on first focus - needed by toolbars..
27340 * @param {HtmlEditor} this
27345 * Auto save the htmlEditor value as a file into Events
27346 * @param {HtmlEditor} this
27350 * @event savedpreview
27351 * preview the saved version of htmlEditor
27352 * @param {HtmlEditor} this
27359 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27363 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27368 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27373 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27378 * @cfg {Number} height (in pixels)
27382 * @cfg {Number} width (in pixels)
27387 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27390 stylesheets: false,
27395 // private properties
27396 validationEvent : false,
27398 initialized : false,
27401 onFocus : Roo.emptyFn,
27403 hideMode:'offsets',
27405 tbContainer : false,
27409 toolbarContainer :function() {
27410 return this.wrap.select('.x-html-editor-tb',true).first();
27414 * Protected method that will not generally be called directly. It
27415 * is called when the editor creates its toolbar. Override this method if you need to
27416 * add custom toolbar buttons.
27417 * @param {HtmlEditor} editor
27419 createToolbar : function(){
27420 Roo.log('renewing');
27421 Roo.log("create toolbars");
27423 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27424 this.toolbars[0].render(this.toolbarContainer());
27428 // if (!editor.toolbars || !editor.toolbars.length) {
27429 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27432 // for (var i =0 ; i < editor.toolbars.length;i++) {
27433 // editor.toolbars[i] = Roo.factory(
27434 // typeof(editor.toolbars[i]) == 'string' ?
27435 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27436 // Roo.bootstrap.HtmlEditor);
27437 // editor.toolbars[i].init(editor);
27443 onRender : function(ct, position)
27445 // Roo.log("Call onRender: " + this.xtype);
27447 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27449 this.wrap = this.inputEl().wrap({
27450 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27453 this.editorcore.onRender(ct, position);
27455 if (this.resizable) {
27456 this.resizeEl = new Roo.Resizable(this.wrap, {
27460 minHeight : this.height,
27461 height: this.height,
27462 handles : this.resizable,
27465 resize : function(r, w, h) {
27466 _t.onResize(w,h); // -something
27472 this.createToolbar(this);
27475 if(!this.width && this.resizable){
27476 this.setSize(this.wrap.getSize());
27478 if (this.resizeEl) {
27479 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27480 // should trigger onReize..
27486 onResize : function(w, h)
27488 Roo.log('resize: ' +w + ',' + h );
27489 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27493 if(this.inputEl() ){
27494 if(typeof w == 'number'){
27495 var aw = w - this.wrap.getFrameWidth('lr');
27496 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27499 if(typeof h == 'number'){
27500 var tbh = -11; // fixme it needs to tool bar size!
27501 for (var i =0; i < this.toolbars.length;i++) {
27502 // fixme - ask toolbars for heights?
27503 tbh += this.toolbars[i].el.getHeight();
27504 //if (this.toolbars[i].footer) {
27505 // tbh += this.toolbars[i].footer.el.getHeight();
27513 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27514 ah -= 5; // knock a few pixes off for look..
27515 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27519 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27520 this.editorcore.onResize(ew,eh);
27525 * Toggles the editor between standard and source edit mode.
27526 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27528 toggleSourceEdit : function(sourceEditMode)
27530 this.editorcore.toggleSourceEdit(sourceEditMode);
27532 if(this.editorcore.sourceEditMode){
27533 Roo.log('editor - showing textarea');
27536 // Roo.log(this.syncValue());
27538 this.inputEl().removeClass(['hide', 'x-hidden']);
27539 this.inputEl().dom.removeAttribute('tabIndex');
27540 this.inputEl().focus();
27542 Roo.log('editor - hiding textarea');
27544 // Roo.log(this.pushValue());
27547 this.inputEl().addClass(['hide', 'x-hidden']);
27548 this.inputEl().dom.setAttribute('tabIndex', -1);
27549 //this.deferFocus();
27552 if(this.resizable){
27553 this.setSize(this.wrap.getSize());
27556 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27559 // private (for BoxComponent)
27560 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27562 // private (for BoxComponent)
27563 getResizeEl : function(){
27567 // private (for BoxComponent)
27568 getPositionEl : function(){
27573 initEvents : function(){
27574 this.originalValue = this.getValue();
27578 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27581 // markInvalid : Roo.emptyFn,
27583 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27586 // clearInvalid : Roo.emptyFn,
27588 setValue : function(v){
27589 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27590 this.editorcore.pushValue();
27595 deferFocus : function(){
27596 this.focus.defer(10, this);
27600 focus : function(){
27601 this.editorcore.focus();
27607 onDestroy : function(){
27613 for (var i =0; i < this.toolbars.length;i++) {
27614 // fixme - ask toolbars for heights?
27615 this.toolbars[i].onDestroy();
27618 this.wrap.dom.innerHTML = '';
27619 this.wrap.remove();
27624 onFirstFocus : function(){
27625 //Roo.log("onFirstFocus");
27626 this.editorcore.onFirstFocus();
27627 for (var i =0; i < this.toolbars.length;i++) {
27628 this.toolbars[i].onFirstFocus();
27634 syncValue : function()
27636 this.editorcore.syncValue();
27639 pushValue : function()
27641 this.editorcore.pushValue();
27645 // hide stuff that is not compatible
27659 * @event specialkey
27663 * @cfg {String} fieldClass @hide
27666 * @cfg {String} focusClass @hide
27669 * @cfg {String} autoCreate @hide
27672 * @cfg {String} inputType @hide
27676 * @cfg {String} invalidText @hide
27679 * @cfg {String} msgFx @hide
27682 * @cfg {String} validateOnBlur @hide
27691 Roo.namespace('Roo.bootstrap.htmleditor');
27693 * @class Roo.bootstrap.HtmlEditorToolbar1
27699 new Roo.bootstrap.HtmlEditor({
27702 new Roo.bootstrap.HtmlEditorToolbar1({
27703 disable : { fonts: 1 , format: 1, ..., ... , ...],
27709 * @cfg {Object} disable List of elements to disable..
27710 * @cfg {Array} btns List of additional buttons.
27714 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27717 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27720 Roo.apply(this, config);
27722 // default disabled, based on 'good practice'..
27723 this.disable = this.disable || {};
27724 Roo.applyIf(this.disable, {
27727 specialElements : true
27729 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27731 this.editor = config.editor;
27732 this.editorcore = config.editor.editorcore;
27734 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27736 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27737 // dont call parent... till later.
27739 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27744 editorcore : false,
27749 "h1","h2","h3","h4","h5","h6",
27751 "abbr", "acronym", "address", "cite", "samp", "var",
27755 onRender : function(ct, position)
27757 // Roo.log("Call onRender: " + this.xtype);
27759 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27761 this.el.dom.style.marginBottom = '0';
27763 var editorcore = this.editorcore;
27764 var editor= this.editor;
27767 var btn = function(id,cmd , toggle, handler, html){
27769 var event = toggle ? 'toggle' : 'click';
27774 xns: Roo.bootstrap,
27778 enableToggle:toggle !== false,
27780 pressed : toggle ? false : null,
27783 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27784 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27790 // var cb_box = function...
27795 xns: Roo.bootstrap,
27800 xns: Roo.bootstrap,
27804 Roo.each(this.formats, function(f) {
27805 style.menu.items.push({
27807 xns: Roo.bootstrap,
27808 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27813 editorcore.insertTag(this.tagname);
27820 children.push(style);
27822 btn('bold',false,true);
27823 btn('italic',false,true);
27824 btn('align-left', 'justifyleft',true);
27825 btn('align-center', 'justifycenter',true);
27826 btn('align-right' , 'justifyright',true);
27827 btn('link', false, false, function(btn) {
27828 //Roo.log("create link?");
27829 var url = prompt(this.createLinkText, this.defaultLinkValue);
27830 if(url && url != 'http:/'+'/'){
27831 this.editorcore.relayCmd('createlink', url);
27834 btn('list','insertunorderedlist',true);
27835 btn('pencil', false,true, function(btn){
27837 this.toggleSourceEdit(btn.pressed);
27840 if (this.editor.btns.length > 0) {
27841 for (var i = 0; i<this.editor.btns.length; i++) {
27842 children.push(this.editor.btns[i]);
27850 xns: Roo.bootstrap,
27855 xns: Roo.bootstrap,
27860 cog.menu.items.push({
27862 xns: Roo.bootstrap,
27863 html : Clean styles,
27868 editorcore.insertTag(this.tagname);
27877 this.xtype = 'NavSimplebar';
27879 for(var i=0;i< children.length;i++) {
27881 this.buttons.add(this.addxtypeChild(children[i]));
27885 editor.on('editorevent', this.updateToolbar, this);
27887 onBtnClick : function(id)
27889 this.editorcore.relayCmd(id);
27890 this.editorcore.focus();
27894 * Protected method that will not generally be called directly. It triggers
27895 * a toolbar update by reading the markup state of the current selection in the editor.
27897 updateToolbar: function(){
27899 if(!this.editorcore.activated){
27900 this.editor.onFirstFocus(); // is this neeed?
27904 var btns = this.buttons;
27905 var doc = this.editorcore.doc;
27906 btns.get('bold').setActive(doc.queryCommandState('bold'));
27907 btns.get('italic').setActive(doc.queryCommandState('italic'));
27908 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27910 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27911 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27912 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27914 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27915 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27918 var ans = this.editorcore.getAllAncestors();
27919 if (this.formatCombo) {
27922 var store = this.formatCombo.store;
27923 this.formatCombo.setValue("");
27924 for (var i =0; i < ans.length;i++) {
27925 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27927 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27935 // hides menus... - so this cant be on a menu...
27936 Roo.bootstrap.MenuMgr.hideAll();
27938 Roo.bootstrap.MenuMgr.hideAll();
27939 //this.editorsyncValue();
27941 onFirstFocus: function() {
27942 this.buttons.each(function(item){
27946 toggleSourceEdit : function(sourceEditMode){
27949 if(sourceEditMode){
27950 Roo.log("disabling buttons");
27951 this.buttons.each( function(item){
27952 if(item.cmd != 'pencil'){
27958 Roo.log("enabling buttons");
27959 if(this.editorcore.initialized){
27960 this.buttons.each( function(item){
27966 Roo.log("calling toggole on editor");
27967 // tell the editor that it's been pressed..
27968 this.editor.toggleSourceEdit(sourceEditMode);
27982 * @class Roo.bootstrap.Markdown
27983 * @extends Roo.bootstrap.TextArea
27984 * Bootstrap Showdown editable area
27985 * @cfg {string} content
27988 * Create a new Showdown
27991 Roo.bootstrap.Markdown = function(config){
27992 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27996 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
28000 initEvents : function()
28003 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28004 this.markdownEl = this.el.createChild({
28005 cls : 'roo-markdown-area'
28007 this.inputEl().addClass('d-none');
28008 if (this.getValue() == '') {
28009 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28012 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28014 this.markdownEl.on('click', this.toggleTextEdit, this);
28015 this.on('blur', this.toggleTextEdit, this);
28016 this.on('specialkey', this.resizeTextArea, this);
28019 toggleTextEdit : function()
28021 var sh = this.markdownEl.getHeight();
28022 this.inputEl().addClass('d-none');
28023 this.markdownEl.addClass('d-none');
28024 if (!this.editing) {
28026 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28027 this.inputEl().removeClass('d-none');
28028 this.inputEl().focus();
28029 this.editing = true;
28032 // show showdown...
28033 this.updateMarkdown();
28034 this.markdownEl.removeClass('d-none');
28035 this.editing = false;
28038 updateMarkdown : function()
28040 if (this.getValue() == '') {
28041 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28045 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28048 resizeTextArea: function () {
28051 Roo.log([sh, this.getValue().split("\n").length * 30]);
28052 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28054 setValue : function(val)
28056 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28057 if (!this.editing) {
28058 this.updateMarkdown();
28064 if (!this.editing) {
28065 this.toggleTextEdit();
28073 * Ext JS Library 1.1.1
28074 * Copyright(c) 2006-2007, Ext JS, LLC.
28076 * Originally Released Under LGPL - original licence link has changed is not relivant.
28079 * <script type="text/javascript">
28083 * @class Roo.bootstrap.PagingToolbar
28084 * @extends Roo.bootstrap.NavSimplebar
28085 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28087 * Create a new PagingToolbar
28088 * @param {Object} config The config object
28089 * @param {Roo.data.Store} store
28091 Roo.bootstrap.PagingToolbar = function(config)
28093 // old args format still supported... - xtype is prefered..
28094 // created from xtype...
28096 this.ds = config.dataSource;
28098 if (config.store && !this.ds) {
28099 this.store= Roo.factory(config.store, Roo.data);
28100 this.ds = this.store;
28101 this.ds.xmodule = this.xmodule || false;
28104 this.toolbarItems = [];
28105 if (config.items) {
28106 this.toolbarItems = config.items;
28109 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28114 this.bind(this.ds);
28117 if (Roo.bootstrap.version == 4) {
28118 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28120 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28125 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28127 * @cfg {Roo.data.Store} dataSource
28128 * The underlying data store providing the paged data
28131 * @cfg {String/HTMLElement/Element} container
28132 * container The id or element that will contain the toolbar
28135 * @cfg {Boolean} displayInfo
28136 * True to display the displayMsg (defaults to false)
28139 * @cfg {Number} pageSize
28140 * The number of records to display per page (defaults to 20)
28144 * @cfg {String} displayMsg
28145 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28147 displayMsg : 'Displaying {0} - {1} of {2}',
28149 * @cfg {String} emptyMsg
28150 * The message to display when no records are found (defaults to "No data to display")
28152 emptyMsg : 'No data to display',
28154 * Customizable piece of the default paging text (defaults to "Page")
28157 beforePageText : "Page",
28159 * Customizable piece of the default paging text (defaults to "of %0")
28162 afterPageText : "of {0}",
28164 * Customizable piece of the default paging text (defaults to "First Page")
28167 firstText : "First Page",
28169 * Customizable piece of the default paging text (defaults to "Previous Page")
28172 prevText : "Previous Page",
28174 * Customizable piece of the default paging text (defaults to "Next Page")
28177 nextText : "Next Page",
28179 * Customizable piece of the default paging text (defaults to "Last Page")
28182 lastText : "Last Page",
28184 * Customizable piece of the default paging text (defaults to "Refresh")
28187 refreshText : "Refresh",
28191 onRender : function(ct, position)
28193 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28194 this.navgroup.parentId = this.id;
28195 this.navgroup.onRender(this.el, null);
28196 // add the buttons to the navgroup
28198 if(this.displayInfo){
28199 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28200 this.displayEl = this.el.select('.x-paging-info', true).first();
28201 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28202 // this.displayEl = navel.el.select('span',true).first();
28208 Roo.each(_this.buttons, function(e){ // this might need to use render????
28209 Roo.factory(e).render(_this.el);
28213 Roo.each(_this.toolbarItems, function(e) {
28214 _this.navgroup.addItem(e);
28218 this.first = this.navgroup.addItem({
28219 tooltip: this.firstText,
28220 cls: "prev btn-outline-secondary",
28221 html : ' <i class="fa fa-step-backward"></i>',
28223 preventDefault: true,
28224 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28227 this.prev = this.navgroup.addItem({
28228 tooltip: this.prevText,
28229 cls: "prev btn-outline-secondary",
28230 html : ' <i class="fa fa-backward"></i>',
28232 preventDefault: true,
28233 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28235 //this.addSeparator();
28238 var field = this.navgroup.addItem( {
28240 cls : 'x-paging-position btn-outline-secondary',
28242 html : this.beforePageText +
28243 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28244 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28247 this.field = field.el.select('input', true).first();
28248 this.field.on("keydown", this.onPagingKeydown, this);
28249 this.field.on("focus", function(){this.dom.select();});
28252 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28253 //this.field.setHeight(18);
28254 //this.addSeparator();
28255 this.next = this.navgroup.addItem({
28256 tooltip: this.nextText,
28257 cls: "next btn-outline-secondary",
28258 html : ' <i class="fa fa-forward"></i>',
28260 preventDefault: true,
28261 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28263 this.last = this.navgroup.addItem({
28264 tooltip: this.lastText,
28265 html : ' <i class="fa fa-step-forward"></i>',
28266 cls: "next btn-outline-secondary",
28268 preventDefault: true,
28269 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28271 //this.addSeparator();
28272 this.loading = this.navgroup.addItem({
28273 tooltip: this.refreshText,
28274 cls: "btn-outline-secondary",
28275 html : ' <i class="fa fa-refresh"></i>',
28276 preventDefault: true,
28277 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28283 updateInfo : function(){
28284 if(this.displayEl){
28285 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28286 var msg = count == 0 ?
28290 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28292 this.displayEl.update(msg);
28297 onLoad : function(ds, r, o)
28299 this.cursor = o.params && o.params.start ? o.params.start : 0;
28301 var d = this.getPageData(),
28306 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28307 this.field.dom.value = ap;
28308 this.first.setDisabled(ap == 1);
28309 this.prev.setDisabled(ap == 1);
28310 this.next.setDisabled(ap == ps);
28311 this.last.setDisabled(ap == ps);
28312 this.loading.enable();
28317 getPageData : function(){
28318 var total = this.ds.getTotalCount();
28321 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28322 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28327 onLoadError : function(){
28328 this.loading.enable();
28332 onPagingKeydown : function(e){
28333 var k = e.getKey();
28334 var d = this.getPageData();
28336 var v = this.field.dom.value, pageNum;
28337 if(!v || isNaN(pageNum = parseInt(v, 10))){
28338 this.field.dom.value = d.activePage;
28341 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28342 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28345 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))
28347 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28348 this.field.dom.value = pageNum;
28349 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28352 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28354 var v = this.field.dom.value, pageNum;
28355 var increment = (e.shiftKey) ? 10 : 1;
28356 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28359 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28360 this.field.dom.value = d.activePage;
28363 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28365 this.field.dom.value = parseInt(v, 10) + increment;
28366 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28367 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28374 beforeLoad : function(){
28376 this.loading.disable();
28381 onClick : function(which){
28390 ds.load({params:{start: 0, limit: this.pageSize}});
28393 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28396 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28399 var total = ds.getTotalCount();
28400 var extra = total % this.pageSize;
28401 var lastStart = extra ? (total - extra) : total-this.pageSize;
28402 ds.load({params:{start: lastStart, limit: this.pageSize}});
28405 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28411 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28412 * @param {Roo.data.Store} store The data store to unbind
28414 unbind : function(ds){
28415 ds.un("beforeload", this.beforeLoad, this);
28416 ds.un("load", this.onLoad, this);
28417 ds.un("loadexception", this.onLoadError, this);
28418 ds.un("remove", this.updateInfo, this);
28419 ds.un("add", this.updateInfo, this);
28420 this.ds = undefined;
28424 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28425 * @param {Roo.data.Store} store The data store to bind
28427 bind : function(ds){
28428 ds.on("beforeload", this.beforeLoad, this);
28429 ds.on("load", this.onLoad, this);
28430 ds.on("loadexception", this.onLoadError, this);
28431 ds.on("remove", this.updateInfo, this);
28432 ds.on("add", this.updateInfo, this);
28443 * @class Roo.bootstrap.MessageBar
28444 * @extends Roo.bootstrap.Component
28445 * Bootstrap MessageBar class
28446 * @cfg {String} html contents of the MessageBar
28447 * @cfg {String} weight (info | success | warning | danger) default info
28448 * @cfg {String} beforeClass insert the bar before the given class
28449 * @cfg {Boolean} closable (true | false) default false
28450 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28453 * Create a new Element
28454 * @param {Object} config The config object
28457 Roo.bootstrap.MessageBar = function(config){
28458 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28461 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28467 beforeClass: 'bootstrap-sticky-wrap',
28469 getAutoCreate : function(){
28473 cls: 'alert alert-dismissable alert-' + this.weight,
28478 html: this.html || ''
28484 cfg.cls += ' alert-messages-fixed';
28498 onRender : function(ct, position)
28500 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28503 var cfg = Roo.apply({}, this.getAutoCreate());
28507 cfg.cls += ' ' + this.cls;
28510 cfg.style = this.style;
28512 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28514 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28517 this.el.select('>button.close').on('click', this.hide, this);
28523 if (!this.rendered) {
28529 this.fireEvent('show', this);
28535 if (!this.rendered) {
28541 this.fireEvent('hide', this);
28544 update : function()
28546 // var e = this.el.dom.firstChild;
28548 // if(this.closable){
28549 // e = e.nextSibling;
28552 // e.data = this.html || '';
28554 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28570 * @class Roo.bootstrap.Graph
28571 * @extends Roo.bootstrap.Component
28572 * Bootstrap Graph class
28576 @cfg {String} graphtype bar | vbar | pie
28577 @cfg {number} g_x coodinator | centre x (pie)
28578 @cfg {number} g_y coodinator | centre y (pie)
28579 @cfg {number} g_r radius (pie)
28580 @cfg {number} g_height height of the chart (respected by all elements in the set)
28581 @cfg {number} g_width width of the chart (respected by all elements in the set)
28582 @cfg {Object} title The title of the chart
28585 -opts (object) options for the chart
28587 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28588 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28590 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.
28591 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28593 o stretch (boolean)
28595 -opts (object) options for the pie
28598 o startAngle (number)
28599 o endAngle (number)
28603 * Create a new Input
28604 * @param {Object} config The config object
28607 Roo.bootstrap.Graph = function(config){
28608 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28614 * The img click event for the img.
28615 * @param {Roo.EventObject} e
28621 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28632 //g_colors: this.colors,
28639 getAutoCreate : function(){
28650 onRender : function(ct,position){
28653 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28655 if (typeof(Raphael) == 'undefined') {
28656 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28660 this.raphael = Raphael(this.el.dom);
28662 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28663 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28664 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28665 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28667 r.text(160, 10, "Single Series Chart").attr(txtattr);
28668 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28669 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28670 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28672 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28673 r.barchart(330, 10, 300, 220, data1);
28674 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28675 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28678 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28679 // r.barchart(30, 30, 560, 250, xdata, {
28680 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28681 // axis : "0 0 1 1",
28682 // axisxlabels : xdata
28683 // //yvalues : cols,
28686 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28688 // this.load(null,xdata,{
28689 // axis : "0 0 1 1",
28690 // axisxlabels : xdata
28695 load : function(graphtype,xdata,opts)
28697 this.raphael.clear();
28699 graphtype = this.graphtype;
28704 var r = this.raphael,
28705 fin = function () {
28706 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28708 fout = function () {
28709 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28711 pfin = function() {
28712 this.sector.stop();
28713 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28716 this.label[0].stop();
28717 this.label[0].attr({ r: 7.5 });
28718 this.label[1].attr({ "font-weight": 800 });
28721 pfout = function() {
28722 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28725 this.label[0].animate({ r: 5 }, 500, "bounce");
28726 this.label[1].attr({ "font-weight": 400 });
28732 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28735 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28738 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28739 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28741 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28748 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28753 setTitle: function(o)
28758 initEvents: function() {
28761 this.el.on('click', this.onClick, this);
28765 onClick : function(e)
28767 Roo.log('img onclick');
28768 this.fireEvent('click', this, e);
28780 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28783 * @class Roo.bootstrap.dash.NumberBox
28784 * @extends Roo.bootstrap.Component
28785 * Bootstrap NumberBox class
28786 * @cfg {String} headline Box headline
28787 * @cfg {String} content Box content
28788 * @cfg {String} icon Box icon
28789 * @cfg {String} footer Footer text
28790 * @cfg {String} fhref Footer href
28793 * Create a new NumberBox
28794 * @param {Object} config The config object
28798 Roo.bootstrap.dash.NumberBox = function(config){
28799 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28803 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28812 getAutoCreate : function(){
28816 cls : 'small-box ',
28824 cls : 'roo-headline',
28825 html : this.headline
28829 cls : 'roo-content',
28830 html : this.content
28844 cls : 'ion ' + this.icon
28853 cls : 'small-box-footer',
28854 href : this.fhref || '#',
28858 cfg.cn.push(footer);
28865 onRender : function(ct,position){
28866 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28873 setHeadline: function (value)
28875 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28878 setFooter: function (value, href)
28880 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28883 this.el.select('a.small-box-footer',true).first().attr('href', href);
28888 setContent: function (value)
28890 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28893 initEvents: function()
28907 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28910 * @class Roo.bootstrap.dash.TabBox
28911 * @extends Roo.bootstrap.Component
28912 * Bootstrap TabBox class
28913 * @cfg {String} title Title of the TabBox
28914 * @cfg {String} icon Icon of the TabBox
28915 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28916 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28919 * Create a new TabBox
28920 * @param {Object} config The config object
28924 Roo.bootstrap.dash.TabBox = function(config){
28925 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28930 * When a pane is added
28931 * @param {Roo.bootstrap.dash.TabPane} pane
28935 * @event activatepane
28936 * When a pane is activated
28937 * @param {Roo.bootstrap.dash.TabPane} pane
28939 "activatepane" : true
28947 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28952 tabScrollable : false,
28954 getChildContainer : function()
28956 return this.el.select('.tab-content', true).first();
28959 getAutoCreate : function(){
28963 cls: 'pull-left header',
28971 cls: 'fa ' + this.icon
28977 cls: 'nav nav-tabs pull-right',
28983 if(this.tabScrollable){
28990 cls: 'nav nav-tabs pull-right',
29001 cls: 'nav-tabs-custom',
29006 cls: 'tab-content no-padding',
29014 initEvents : function()
29016 //Roo.log('add add pane handler');
29017 this.on('addpane', this.onAddPane, this);
29020 * Updates the box title
29021 * @param {String} html to set the title to.
29023 setTitle : function(value)
29025 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29027 onAddPane : function(pane)
29029 this.panes.push(pane);
29030 //Roo.log('addpane');
29032 // tabs are rendere left to right..
29033 if(!this.showtabs){
29037 var ctr = this.el.select('.nav-tabs', true).first();
29040 var existing = ctr.select('.nav-tab',true);
29041 var qty = existing.getCount();;
29044 var tab = ctr.createChild({
29046 cls : 'nav-tab' + (qty ? '' : ' active'),
29054 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29057 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29059 pane.el.addClass('active');
29064 onTabClick : function(ev,un,ob,pane)
29066 //Roo.log('tab - prev default');
29067 ev.preventDefault();
29070 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29071 pane.tab.addClass('active');
29072 //Roo.log(pane.title);
29073 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29074 // technically we should have a deactivate event.. but maybe add later.
29075 // and it should not de-activate the selected tab...
29076 this.fireEvent('activatepane', pane);
29077 pane.el.addClass('active');
29078 pane.fireEvent('activate');
29083 getActivePane : function()
29086 Roo.each(this.panes, function(p) {
29087 if(p.el.hasClass('active')){
29108 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29110 * @class Roo.bootstrap.TabPane
29111 * @extends Roo.bootstrap.Component
29112 * Bootstrap TabPane class
29113 * @cfg {Boolean} active (false | true) Default false
29114 * @cfg {String} title title of panel
29118 * Create a new TabPane
29119 * @param {Object} config The config object
29122 Roo.bootstrap.dash.TabPane = function(config){
29123 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29129 * When a pane is activated
29130 * @param {Roo.bootstrap.dash.TabPane} pane
29137 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29142 // the tabBox that this is attached to.
29145 getAutoCreate : function()
29153 cfg.cls += ' active';
29158 initEvents : function()
29160 //Roo.log('trigger add pane handler');
29161 this.parent().fireEvent('addpane', this)
29165 * Updates the tab title
29166 * @param {String} html to set the title to.
29168 setTitle: function(str)
29174 this.tab.select('a', true).first().dom.innerHTML = str;
29191 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29194 * @class Roo.bootstrap.menu.Menu
29195 * @extends Roo.bootstrap.Component
29196 * Bootstrap Menu class - container for Menu
29197 * @cfg {String} html Text of the menu
29198 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29199 * @cfg {String} icon Font awesome icon
29200 * @cfg {String} pos Menu align to (top | bottom) default bottom
29204 * Create a new Menu
29205 * @param {Object} config The config object
29209 Roo.bootstrap.menu.Menu = function(config){
29210 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29214 * @event beforeshow
29215 * Fires before this menu is displayed
29216 * @param {Roo.bootstrap.menu.Menu} this
29220 * @event beforehide
29221 * Fires before this menu is hidden
29222 * @param {Roo.bootstrap.menu.Menu} this
29227 * Fires after this menu is displayed
29228 * @param {Roo.bootstrap.menu.Menu} this
29233 * Fires after this menu is hidden
29234 * @param {Roo.bootstrap.menu.Menu} this
29239 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29240 * @param {Roo.bootstrap.menu.Menu} this
29241 * @param {Roo.EventObject} e
29248 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29252 weight : 'default',
29257 getChildContainer : function() {
29258 if(this.isSubMenu){
29262 return this.el.select('ul.dropdown-menu', true).first();
29265 getAutoCreate : function()
29270 cls : 'roo-menu-text',
29278 cls : 'fa ' + this.icon
29289 cls : 'dropdown-button btn btn-' + this.weight,
29294 cls : 'dropdown-toggle btn btn-' + this.weight,
29304 cls : 'dropdown-menu'
29310 if(this.pos == 'top'){
29311 cfg.cls += ' dropup';
29314 if(this.isSubMenu){
29317 cls : 'dropdown-menu'
29324 onRender : function(ct, position)
29326 this.isSubMenu = ct.hasClass('dropdown-submenu');
29328 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29331 initEvents : function()
29333 if(this.isSubMenu){
29337 this.hidden = true;
29339 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29340 this.triggerEl.on('click', this.onTriggerPress, this);
29342 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29343 this.buttonEl.on('click', this.onClick, this);
29349 if(this.isSubMenu){
29353 return this.el.select('ul.dropdown-menu', true).first();
29356 onClick : function(e)
29358 this.fireEvent("click", this, e);
29361 onTriggerPress : function(e)
29363 if (this.isVisible()) {
29370 isVisible : function(){
29371 return !this.hidden;
29376 this.fireEvent("beforeshow", this);
29378 this.hidden = false;
29379 this.el.addClass('open');
29381 Roo.get(document).on("mouseup", this.onMouseUp, this);
29383 this.fireEvent("show", this);
29390 this.fireEvent("beforehide", this);
29392 this.hidden = true;
29393 this.el.removeClass('open');
29395 Roo.get(document).un("mouseup", this.onMouseUp);
29397 this.fireEvent("hide", this);
29400 onMouseUp : function()
29414 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29417 * @class Roo.bootstrap.menu.Item
29418 * @extends Roo.bootstrap.Component
29419 * Bootstrap MenuItem class
29420 * @cfg {Boolean} submenu (true | false) default false
29421 * @cfg {String} html text of the item
29422 * @cfg {String} href the link
29423 * @cfg {Boolean} disable (true | false) default false
29424 * @cfg {Boolean} preventDefault (true | false) default true
29425 * @cfg {String} icon Font awesome icon
29426 * @cfg {String} pos Submenu align to (left | right) default right
29430 * Create a new Item
29431 * @param {Object} config The config object
29435 Roo.bootstrap.menu.Item = function(config){
29436 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29440 * Fires when the mouse is hovering over this menu
29441 * @param {Roo.bootstrap.menu.Item} this
29442 * @param {Roo.EventObject} e
29447 * Fires when the mouse exits this menu
29448 * @param {Roo.bootstrap.menu.Item} this
29449 * @param {Roo.EventObject} e
29455 * The raw click event for the entire grid.
29456 * @param {Roo.EventObject} e
29462 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29467 preventDefault: true,
29472 getAutoCreate : function()
29477 cls : 'roo-menu-item-text',
29485 cls : 'fa ' + this.icon
29494 href : this.href || '#',
29501 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29505 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29507 if(this.pos == 'left'){
29508 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29515 initEvents : function()
29517 this.el.on('mouseover', this.onMouseOver, this);
29518 this.el.on('mouseout', this.onMouseOut, this);
29520 this.el.select('a', true).first().on('click', this.onClick, this);
29524 onClick : function(e)
29526 if(this.preventDefault){
29527 e.preventDefault();
29530 this.fireEvent("click", this, e);
29533 onMouseOver : function(e)
29535 if(this.submenu && this.pos == 'left'){
29536 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29539 this.fireEvent("mouseover", this, e);
29542 onMouseOut : function(e)
29544 this.fireEvent("mouseout", this, e);
29556 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29559 * @class Roo.bootstrap.menu.Separator
29560 * @extends Roo.bootstrap.Component
29561 * Bootstrap Separator class
29564 * Create a new Separator
29565 * @param {Object} config The config object
29569 Roo.bootstrap.menu.Separator = function(config){
29570 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29573 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29575 getAutoCreate : function(){
29578 cls: 'dropdown-divider divider'
29596 * @class Roo.bootstrap.Tooltip
29597 * Bootstrap Tooltip class
29598 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29599 * to determine which dom element triggers the tooltip.
29601 * It needs to add support for additional attributes like tooltip-position
29604 * Create a new Toolti
29605 * @param {Object} config The config object
29608 Roo.bootstrap.Tooltip = function(config){
29609 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29611 this.alignment = Roo.bootstrap.Tooltip.alignment;
29613 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29614 this.alignment = config.alignment;
29619 Roo.apply(Roo.bootstrap.Tooltip, {
29621 * @function init initialize tooltip monitoring.
29625 currentTip : false,
29626 currentRegion : false,
29632 Roo.get(document).on('mouseover', this.enter ,this);
29633 Roo.get(document).on('mouseout', this.leave, this);
29636 this.currentTip = new Roo.bootstrap.Tooltip();
29639 enter : function(ev)
29641 var dom = ev.getTarget();
29643 //Roo.log(['enter',dom]);
29644 var el = Roo.fly(dom);
29645 if (this.currentEl) {
29647 //Roo.log(this.currentEl);
29648 //Roo.log(this.currentEl.contains(dom));
29649 if (this.currentEl == el) {
29652 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29658 if (this.currentTip.el) {
29659 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29663 if(!el || el.dom == document){
29669 if (!el.attr('tooltip')) {
29670 pel = el.findParent("[tooltip]");
29672 bindEl = Roo.get(pel);
29678 // you can not look for children, as if el is the body.. then everythign is the child..
29679 if (!pel && !el.attr('tooltip')) { //
29680 if (!el.select("[tooltip]").elements.length) {
29683 // is the mouse over this child...?
29684 bindEl = el.select("[tooltip]").first();
29685 var xy = ev.getXY();
29686 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29687 //Roo.log("not in region.");
29690 //Roo.log("child element over..");
29693 this.currentEl = el;
29694 this.currentTip.bind(bindEl);
29695 this.currentRegion = Roo.lib.Region.getRegion(dom);
29696 this.currentTip.enter();
29699 leave : function(ev)
29701 var dom = ev.getTarget();
29702 //Roo.log(['leave',dom]);
29703 if (!this.currentEl) {
29708 if (dom != this.currentEl.dom) {
29711 var xy = ev.getXY();
29712 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29715 // only activate leave if mouse cursor is outside... bounding box..
29720 if (this.currentTip) {
29721 this.currentTip.leave();
29723 //Roo.log('clear currentEl');
29724 this.currentEl = false;
29729 'left' : ['r-l', [-2,0], 'right'],
29730 'right' : ['l-r', [2,0], 'left'],
29731 'bottom' : ['t-b', [0,2], 'top'],
29732 'top' : [ 'b-t', [0,-2], 'bottom']
29738 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29743 delay : null, // can be { show : 300 , hide: 500}
29747 hoverState : null, //???
29749 placement : 'bottom',
29753 getAutoCreate : function(){
29760 cls : 'tooltip-arrow arrow'
29763 cls : 'tooltip-inner'
29770 bind : function(el)
29775 initEvents : function()
29777 this.arrowEl = this.el.select('.arrow', true).first();
29778 this.innerEl = this.el.select('.tooltip-inner', true).first();
29781 enter : function () {
29783 if (this.timeout != null) {
29784 clearTimeout(this.timeout);
29787 this.hoverState = 'in';
29788 //Roo.log("enter - show");
29789 if (!this.delay || !this.delay.show) {
29794 this.timeout = setTimeout(function () {
29795 if (_t.hoverState == 'in') {
29798 }, this.delay.show);
29802 clearTimeout(this.timeout);
29804 this.hoverState = 'out';
29805 if (!this.delay || !this.delay.hide) {
29811 this.timeout = setTimeout(function () {
29812 //Roo.log("leave - timeout");
29814 if (_t.hoverState == 'out') {
29816 Roo.bootstrap.Tooltip.currentEl = false;
29821 show : function (msg)
29824 this.render(document.body);
29827 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29829 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29831 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29833 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29834 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29836 var placement = typeof this.placement == 'function' ?
29837 this.placement.call(this, this.el, on_el) :
29840 var autoToken = /\s?auto?\s?/i;
29841 var autoPlace = autoToken.test(placement);
29843 placement = placement.replace(autoToken, '') || 'top';
29847 //this.el.setXY([0,0]);
29849 //this.el.dom.style.display='block';
29851 //this.el.appendTo(on_el);
29853 var p = this.getPosition();
29854 var box = this.el.getBox();
29860 var align = this.alignment[placement];
29862 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29864 if(placement == 'top' || placement == 'bottom'){
29866 placement = 'right';
29869 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29870 placement = 'left';
29873 var scroll = Roo.select('body', true).first().getScroll();
29875 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29879 align = this.alignment[placement];
29881 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29885 var elems = document.getElementsByTagName('div');
29886 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29887 for (var i = 0; i < elems.length; i++) {
29888 var zindex = Number.parseInt(
29889 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29892 if (zindex > highest) {
29899 this.el.dom.style.zIndex = highest;
29901 this.el.alignTo(this.bindEl, align[0],align[1]);
29902 //var arrow = this.el.select('.arrow',true).first();
29903 //arrow.set(align[2],
29905 this.el.addClass(placement);
29906 this.el.addClass("bs-tooltip-"+ placement);
29908 this.el.addClass('in fade show');
29910 this.hoverState = null;
29912 if (this.el.hasClass('fade')) {
29927 //this.el.setXY([0,0]);
29928 this.el.removeClass(['show', 'in']);
29944 * @class Roo.bootstrap.LocationPicker
29945 * @extends Roo.bootstrap.Component
29946 * Bootstrap LocationPicker class
29947 * @cfg {Number} latitude Position when init default 0
29948 * @cfg {Number} longitude Position when init default 0
29949 * @cfg {Number} zoom default 15
29950 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29951 * @cfg {Boolean} mapTypeControl default false
29952 * @cfg {Boolean} disableDoubleClickZoom default false
29953 * @cfg {Boolean} scrollwheel default true
29954 * @cfg {Boolean} streetViewControl default false
29955 * @cfg {Number} radius default 0
29956 * @cfg {String} locationName
29957 * @cfg {Boolean} draggable default true
29958 * @cfg {Boolean} enableAutocomplete default false
29959 * @cfg {Boolean} enableReverseGeocode default true
29960 * @cfg {String} markerTitle
29963 * Create a new LocationPicker
29964 * @param {Object} config The config object
29968 Roo.bootstrap.LocationPicker = function(config){
29970 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29975 * Fires when the picker initialized.
29976 * @param {Roo.bootstrap.LocationPicker} this
29977 * @param {Google Location} location
29981 * @event positionchanged
29982 * Fires when the picker position changed.
29983 * @param {Roo.bootstrap.LocationPicker} this
29984 * @param {Google Location} location
29986 positionchanged : true,
29989 * Fires when the map resize.
29990 * @param {Roo.bootstrap.LocationPicker} this
29995 * Fires when the map show.
29996 * @param {Roo.bootstrap.LocationPicker} this
30001 * Fires when the map hide.
30002 * @param {Roo.bootstrap.LocationPicker} this
30007 * Fires when click the map.
30008 * @param {Roo.bootstrap.LocationPicker} this
30009 * @param {Map event} e
30013 * @event mapRightClick
30014 * Fires when right click the map.
30015 * @param {Roo.bootstrap.LocationPicker} this
30016 * @param {Map event} e
30018 mapRightClick : true,
30020 * @event markerClick
30021 * Fires when click the marker.
30022 * @param {Roo.bootstrap.LocationPicker} this
30023 * @param {Map event} e
30025 markerClick : true,
30027 * @event markerRightClick
30028 * Fires when right click the marker.
30029 * @param {Roo.bootstrap.LocationPicker} this
30030 * @param {Map event} e
30032 markerRightClick : true,
30034 * @event OverlayViewDraw
30035 * Fires when OverlayView Draw
30036 * @param {Roo.bootstrap.LocationPicker} this
30038 OverlayViewDraw : true,
30040 * @event OverlayViewOnAdd
30041 * Fires when OverlayView Draw
30042 * @param {Roo.bootstrap.LocationPicker} this
30044 OverlayViewOnAdd : true,
30046 * @event OverlayViewOnRemove
30047 * Fires when OverlayView Draw
30048 * @param {Roo.bootstrap.LocationPicker} this
30050 OverlayViewOnRemove : true,
30052 * @event OverlayViewShow
30053 * Fires when OverlayView Draw
30054 * @param {Roo.bootstrap.LocationPicker} this
30055 * @param {Pixel} cpx
30057 OverlayViewShow : true,
30059 * @event OverlayViewHide
30060 * Fires when OverlayView Draw
30061 * @param {Roo.bootstrap.LocationPicker} this
30063 OverlayViewHide : true,
30065 * @event loadexception
30066 * Fires when load google lib failed.
30067 * @param {Roo.bootstrap.LocationPicker} this
30069 loadexception : true
30074 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30076 gMapContext: false,
30082 mapTypeControl: false,
30083 disableDoubleClickZoom: false,
30085 streetViewControl: false,
30089 enableAutocomplete: false,
30090 enableReverseGeocode: true,
30093 getAutoCreate: function()
30098 cls: 'roo-location-picker'
30104 initEvents: function(ct, position)
30106 if(!this.el.getWidth() || this.isApplied()){
30110 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30115 initial: function()
30117 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30118 this.fireEvent('loadexception', this);
30122 if(!this.mapTypeId){
30123 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30126 this.gMapContext = this.GMapContext();
30128 this.initOverlayView();
30130 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30134 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30135 _this.setPosition(_this.gMapContext.marker.position);
30138 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30139 _this.fireEvent('mapClick', this, event);
30143 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30144 _this.fireEvent('mapRightClick', this, event);
30148 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30149 _this.fireEvent('markerClick', this, event);
30153 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30154 _this.fireEvent('markerRightClick', this, event);
30158 this.setPosition(this.gMapContext.location);
30160 this.fireEvent('initial', this, this.gMapContext.location);
30163 initOverlayView: function()
30167 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30171 _this.fireEvent('OverlayViewDraw', _this);
30176 _this.fireEvent('OverlayViewOnAdd', _this);
30179 onRemove: function()
30181 _this.fireEvent('OverlayViewOnRemove', _this);
30184 show: function(cpx)
30186 _this.fireEvent('OverlayViewShow', _this, cpx);
30191 _this.fireEvent('OverlayViewHide', _this);
30197 fromLatLngToContainerPixel: function(event)
30199 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30202 isApplied: function()
30204 return this.getGmapContext() == false ? false : true;
30207 getGmapContext: function()
30209 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30212 GMapContext: function()
30214 var position = new google.maps.LatLng(this.latitude, this.longitude);
30216 var _map = new google.maps.Map(this.el.dom, {
30219 mapTypeId: this.mapTypeId,
30220 mapTypeControl: this.mapTypeControl,
30221 disableDoubleClickZoom: this.disableDoubleClickZoom,
30222 scrollwheel: this.scrollwheel,
30223 streetViewControl: this.streetViewControl,
30224 locationName: this.locationName,
30225 draggable: this.draggable,
30226 enableAutocomplete: this.enableAutocomplete,
30227 enableReverseGeocode: this.enableReverseGeocode
30230 var _marker = new google.maps.Marker({
30231 position: position,
30233 title: this.markerTitle,
30234 draggable: this.draggable
30241 location: position,
30242 radius: this.radius,
30243 locationName: this.locationName,
30244 addressComponents: {
30245 formatted_address: null,
30246 addressLine1: null,
30247 addressLine2: null,
30249 streetNumber: null,
30253 stateOrProvince: null
30256 domContainer: this.el.dom,
30257 geodecoder: new google.maps.Geocoder()
30261 drawCircle: function(center, radius, options)
30263 if (this.gMapContext.circle != null) {
30264 this.gMapContext.circle.setMap(null);
30268 options = Roo.apply({}, options, {
30269 strokeColor: "#0000FF",
30270 strokeOpacity: .35,
30272 fillColor: "#0000FF",
30276 options.map = this.gMapContext.map;
30277 options.radius = radius;
30278 options.center = center;
30279 this.gMapContext.circle = new google.maps.Circle(options);
30280 return this.gMapContext.circle;
30286 setPosition: function(location)
30288 this.gMapContext.location = location;
30289 this.gMapContext.marker.setPosition(location);
30290 this.gMapContext.map.panTo(location);
30291 this.drawCircle(location, this.gMapContext.radius, {});
30295 if (this.gMapContext.settings.enableReverseGeocode) {
30296 this.gMapContext.geodecoder.geocode({
30297 latLng: this.gMapContext.location
30298 }, function(results, status) {
30300 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30301 _this.gMapContext.locationName = results[0].formatted_address;
30302 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30304 _this.fireEvent('positionchanged', this, location);
30311 this.fireEvent('positionchanged', this, location);
30316 google.maps.event.trigger(this.gMapContext.map, "resize");
30318 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30320 this.fireEvent('resize', this);
30323 setPositionByLatLng: function(latitude, longitude)
30325 this.setPosition(new google.maps.LatLng(latitude, longitude));
30328 getCurrentPosition: function()
30331 latitude: this.gMapContext.location.lat(),
30332 longitude: this.gMapContext.location.lng()
30336 getAddressName: function()
30338 return this.gMapContext.locationName;
30341 getAddressComponents: function()
30343 return this.gMapContext.addressComponents;
30346 address_component_from_google_geocode: function(address_components)
30350 for (var i = 0; i < address_components.length; i++) {
30351 var component = address_components[i];
30352 if (component.types.indexOf("postal_code") >= 0) {
30353 result.postalCode = component.short_name;
30354 } else if (component.types.indexOf("street_number") >= 0) {
30355 result.streetNumber = component.short_name;
30356 } else if (component.types.indexOf("route") >= 0) {
30357 result.streetName = component.short_name;
30358 } else if (component.types.indexOf("neighborhood") >= 0) {
30359 result.city = component.short_name;
30360 } else if (component.types.indexOf("locality") >= 0) {
30361 result.city = component.short_name;
30362 } else if (component.types.indexOf("sublocality") >= 0) {
30363 result.district = component.short_name;
30364 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30365 result.stateOrProvince = component.short_name;
30366 } else if (component.types.indexOf("country") >= 0) {
30367 result.country = component.short_name;
30371 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30372 result.addressLine2 = "";
30376 setZoomLevel: function(zoom)
30378 this.gMapContext.map.setZoom(zoom);
30391 this.fireEvent('show', this);
30402 this.fireEvent('hide', this);
30407 Roo.apply(Roo.bootstrap.LocationPicker, {
30409 OverlayView : function(map, options)
30411 options = options || {};
30418 * @class Roo.bootstrap.Alert
30419 * @extends Roo.bootstrap.Component
30420 * Bootstrap Alert class - shows an alert area box
30422 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30423 Enter a valid email address
30426 * @cfg {String} title The title of alert
30427 * @cfg {String} html The content of alert
30428 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30429 * @cfg {String} fa font-awesomeicon
30430 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30431 * @cfg {Boolean} close true to show a x closer
30435 * Create a new alert
30436 * @param {Object} config The config object
30440 Roo.bootstrap.Alert = function(config){
30441 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30445 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30451 faicon: false, // BC
30455 getAutoCreate : function()
30467 style : this.close ? '' : 'display:none'
30471 cls : 'roo-alert-icon'
30476 cls : 'roo-alert-title',
30481 cls : 'roo-alert-text',
30488 cfg.cn[0].cls += ' fa ' + this.faicon;
30491 cfg.cn[0].cls += ' fa ' + this.fa;
30495 cfg.cls += ' alert-' + this.weight;
30501 initEvents: function()
30503 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30504 this.titleEl = this.el.select('.roo-alert-title',true).first();
30505 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30506 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30507 if (this.seconds > 0) {
30508 this.hide.defer(this.seconds, this);
30512 * Set the Title Message HTML
30513 * @param {String} html
30515 setTitle : function(str)
30517 this.titleEl.dom.innerHTML = str;
30521 * Set the Body Message HTML
30522 * @param {String} html
30524 setHtml : function(str)
30526 this.htmlEl.dom.innerHTML = str;
30529 * Set the Weight of the alert
30530 * @param {String} (success|info|warning|danger) weight
30533 setWeight : function(weight)
30536 this.el.removeClass('alert-' + this.weight);
30539 this.weight = weight;
30541 this.el.addClass('alert-' + this.weight);
30544 * Set the Icon of the alert
30545 * @param {String} see fontawsome names (name without the 'fa-' bit)
30547 setIcon : function(icon)
30550 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30553 this.faicon = icon;
30555 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30580 * @class Roo.bootstrap.UploadCropbox
30581 * @extends Roo.bootstrap.Component
30582 * Bootstrap UploadCropbox class
30583 * @cfg {String} emptyText show when image has been loaded
30584 * @cfg {String} rotateNotify show when image too small to rotate
30585 * @cfg {Number} errorTimeout default 3000
30586 * @cfg {Number} minWidth default 300
30587 * @cfg {Number} minHeight default 300
30588 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30589 * @cfg {Boolean} isDocument (true|false) default false
30590 * @cfg {String} url action url
30591 * @cfg {String} paramName default 'imageUpload'
30592 * @cfg {String} method default POST
30593 * @cfg {Boolean} loadMask (true|false) default true
30594 * @cfg {Boolean} loadingText default 'Loading...'
30597 * Create a new UploadCropbox
30598 * @param {Object} config The config object
30601 Roo.bootstrap.UploadCropbox = function(config){
30602 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30606 * @event beforeselectfile
30607 * Fire before select file
30608 * @param {Roo.bootstrap.UploadCropbox} this
30610 "beforeselectfile" : true,
30613 * Fire after initEvent
30614 * @param {Roo.bootstrap.UploadCropbox} this
30619 * Fire after initEvent
30620 * @param {Roo.bootstrap.UploadCropbox} this
30621 * @param {String} data
30626 * Fire when preparing the file data
30627 * @param {Roo.bootstrap.UploadCropbox} this
30628 * @param {Object} file
30633 * Fire when get exception
30634 * @param {Roo.bootstrap.UploadCropbox} this
30635 * @param {XMLHttpRequest} xhr
30637 "exception" : true,
30639 * @event beforeloadcanvas
30640 * Fire before load the canvas
30641 * @param {Roo.bootstrap.UploadCropbox} this
30642 * @param {String} src
30644 "beforeloadcanvas" : true,
30647 * Fire when trash image
30648 * @param {Roo.bootstrap.UploadCropbox} this
30653 * Fire when download the image
30654 * @param {Roo.bootstrap.UploadCropbox} this
30658 * @event footerbuttonclick
30659 * Fire when footerbuttonclick
30660 * @param {Roo.bootstrap.UploadCropbox} this
30661 * @param {String} type
30663 "footerbuttonclick" : true,
30667 * @param {Roo.bootstrap.UploadCropbox} this
30672 * Fire when rotate the image
30673 * @param {Roo.bootstrap.UploadCropbox} this
30674 * @param {String} pos
30679 * Fire when inspect the file
30680 * @param {Roo.bootstrap.UploadCropbox} this
30681 * @param {Object} file
30686 * Fire when xhr upload the file
30687 * @param {Roo.bootstrap.UploadCropbox} this
30688 * @param {Object} data
30693 * Fire when arrange the file data
30694 * @param {Roo.bootstrap.UploadCropbox} this
30695 * @param {Object} formData
30700 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30703 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30705 emptyText : 'Click to upload image',
30706 rotateNotify : 'Image is too small to rotate',
30707 errorTimeout : 3000,
30721 cropType : 'image/jpeg',
30723 canvasLoaded : false,
30724 isDocument : false,
30726 paramName : 'imageUpload',
30728 loadingText : 'Loading...',
30731 getAutoCreate : function()
30735 cls : 'roo-upload-cropbox',
30739 cls : 'roo-upload-cropbox-selector',
30744 cls : 'roo-upload-cropbox-body',
30745 style : 'cursor:pointer',
30749 cls : 'roo-upload-cropbox-preview'
30753 cls : 'roo-upload-cropbox-thumb'
30757 cls : 'roo-upload-cropbox-empty-notify',
30758 html : this.emptyText
30762 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30763 html : this.rotateNotify
30769 cls : 'roo-upload-cropbox-footer',
30772 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30782 onRender : function(ct, position)
30784 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30786 if (this.buttons.length) {
30788 Roo.each(this.buttons, function(bb) {
30790 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30792 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30798 this.maskEl = this.el;
30802 initEvents : function()
30804 this.urlAPI = (window.createObjectURL && window) ||
30805 (window.URL && URL.revokeObjectURL && URL) ||
30806 (window.webkitURL && webkitURL);
30808 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30809 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30811 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30812 this.selectorEl.hide();
30814 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30815 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30817 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30818 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30819 this.thumbEl.hide();
30821 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30822 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30824 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30825 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30826 this.errorEl.hide();
30828 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30829 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30830 this.footerEl.hide();
30832 this.setThumbBoxSize();
30838 this.fireEvent('initial', this);
30845 window.addEventListener("resize", function() { _this.resize(); } );
30847 this.bodyEl.on('click', this.beforeSelectFile, this);
30850 this.bodyEl.on('touchstart', this.onTouchStart, this);
30851 this.bodyEl.on('touchmove', this.onTouchMove, this);
30852 this.bodyEl.on('touchend', this.onTouchEnd, this);
30856 this.bodyEl.on('mousedown', this.onMouseDown, this);
30857 this.bodyEl.on('mousemove', this.onMouseMove, this);
30858 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30859 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30860 Roo.get(document).on('mouseup', this.onMouseUp, this);
30863 this.selectorEl.on('change', this.onFileSelected, this);
30869 this.baseScale = 1;
30871 this.baseRotate = 1;
30872 this.dragable = false;
30873 this.pinching = false;
30876 this.cropData = false;
30877 this.notifyEl.dom.innerHTML = this.emptyText;
30879 this.selectorEl.dom.value = '';
30883 resize : function()
30885 if(this.fireEvent('resize', this) != false){
30886 this.setThumbBoxPosition();
30887 this.setCanvasPosition();
30891 onFooterButtonClick : function(e, el, o, type)
30894 case 'rotate-left' :
30895 this.onRotateLeft(e);
30897 case 'rotate-right' :
30898 this.onRotateRight(e);
30901 this.beforeSelectFile(e);
30916 this.fireEvent('footerbuttonclick', this, type);
30919 beforeSelectFile : function(e)
30921 e.preventDefault();
30923 if(this.fireEvent('beforeselectfile', this) != false){
30924 this.selectorEl.dom.click();
30928 onFileSelected : function(e)
30930 e.preventDefault();
30932 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30936 var file = this.selectorEl.dom.files[0];
30938 if(this.fireEvent('inspect', this, file) != false){
30939 this.prepare(file);
30944 trash : function(e)
30946 this.fireEvent('trash', this);
30949 download : function(e)
30951 this.fireEvent('download', this);
30954 loadCanvas : function(src)
30956 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30960 this.imageEl = document.createElement('img');
30964 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30966 this.imageEl.src = src;
30970 onLoadCanvas : function()
30972 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30973 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30975 this.bodyEl.un('click', this.beforeSelectFile, this);
30977 this.notifyEl.hide();
30978 this.thumbEl.show();
30979 this.footerEl.show();
30981 this.baseRotateLevel();
30983 if(this.isDocument){
30984 this.setThumbBoxSize();
30987 this.setThumbBoxPosition();
30989 this.baseScaleLevel();
30995 this.canvasLoaded = true;
30998 this.maskEl.unmask();
31003 setCanvasPosition : function()
31005 if(!this.canvasEl){
31009 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31010 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31012 this.previewEl.setLeft(pw);
31013 this.previewEl.setTop(ph);
31017 onMouseDown : function(e)
31021 this.dragable = true;
31022 this.pinching = false;
31024 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31025 this.dragable = false;
31029 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31030 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31034 onMouseMove : function(e)
31038 if(!this.canvasLoaded){
31042 if (!this.dragable){
31046 var minX = Math.ceil(this.thumbEl.getLeft(true));
31047 var minY = Math.ceil(this.thumbEl.getTop(true));
31049 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31050 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31052 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31053 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31055 x = x - this.mouseX;
31056 y = y - this.mouseY;
31058 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31059 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31061 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31062 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31064 this.previewEl.setLeft(bgX);
31065 this.previewEl.setTop(bgY);
31067 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31068 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31071 onMouseUp : function(e)
31075 this.dragable = false;
31078 onMouseWheel : function(e)
31082 this.startScale = this.scale;
31084 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31086 if(!this.zoomable()){
31087 this.scale = this.startScale;
31096 zoomable : function()
31098 var minScale = this.thumbEl.getWidth() / this.minWidth;
31100 if(this.minWidth < this.minHeight){
31101 minScale = this.thumbEl.getHeight() / this.minHeight;
31104 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31105 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31109 (this.rotate == 0 || this.rotate == 180) &&
31111 width > this.imageEl.OriginWidth ||
31112 height > this.imageEl.OriginHeight ||
31113 (width < this.minWidth && height < this.minHeight)
31121 (this.rotate == 90 || this.rotate == 270) &&
31123 width > this.imageEl.OriginWidth ||
31124 height > this.imageEl.OriginHeight ||
31125 (width < this.minHeight && height < this.minWidth)
31132 !this.isDocument &&
31133 (this.rotate == 0 || this.rotate == 180) &&
31135 width < this.minWidth ||
31136 width > this.imageEl.OriginWidth ||
31137 height < this.minHeight ||
31138 height > this.imageEl.OriginHeight
31145 !this.isDocument &&
31146 (this.rotate == 90 || this.rotate == 270) &&
31148 width < this.minHeight ||
31149 width > this.imageEl.OriginWidth ||
31150 height < this.minWidth ||
31151 height > this.imageEl.OriginHeight
31161 onRotateLeft : function(e)
31163 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31165 var minScale = this.thumbEl.getWidth() / this.minWidth;
31167 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31168 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31170 this.startScale = this.scale;
31172 while (this.getScaleLevel() < minScale){
31174 this.scale = this.scale + 1;
31176 if(!this.zoomable()){
31181 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31182 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31187 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31194 this.scale = this.startScale;
31196 this.onRotateFail();
31201 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31203 if(this.isDocument){
31204 this.setThumbBoxSize();
31205 this.setThumbBoxPosition();
31206 this.setCanvasPosition();
31211 this.fireEvent('rotate', this, 'left');
31215 onRotateRight : function(e)
31217 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31219 var minScale = this.thumbEl.getWidth() / this.minWidth;
31221 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31222 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31224 this.startScale = this.scale;
31226 while (this.getScaleLevel() < minScale){
31228 this.scale = this.scale + 1;
31230 if(!this.zoomable()){
31235 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31236 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31241 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31248 this.scale = this.startScale;
31250 this.onRotateFail();
31255 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31257 if(this.isDocument){
31258 this.setThumbBoxSize();
31259 this.setThumbBoxPosition();
31260 this.setCanvasPosition();
31265 this.fireEvent('rotate', this, 'right');
31268 onRotateFail : function()
31270 this.errorEl.show(true);
31274 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31279 this.previewEl.dom.innerHTML = '';
31281 var canvasEl = document.createElement("canvas");
31283 var contextEl = canvasEl.getContext("2d");
31285 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31286 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31287 var center = this.imageEl.OriginWidth / 2;
31289 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31290 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31291 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31292 center = this.imageEl.OriginHeight / 2;
31295 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31297 contextEl.translate(center, center);
31298 contextEl.rotate(this.rotate * Math.PI / 180);
31300 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31302 this.canvasEl = document.createElement("canvas");
31304 this.contextEl = this.canvasEl.getContext("2d");
31306 switch (this.rotate) {
31309 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31310 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31312 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31317 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31318 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31320 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31321 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);
31325 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31330 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31331 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31333 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31334 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);
31338 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);
31343 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31344 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31346 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31347 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31351 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);
31358 this.previewEl.appendChild(this.canvasEl);
31360 this.setCanvasPosition();
31365 if(!this.canvasLoaded){
31369 var imageCanvas = document.createElement("canvas");
31371 var imageContext = imageCanvas.getContext("2d");
31373 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31374 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31376 var center = imageCanvas.width / 2;
31378 imageContext.translate(center, center);
31380 imageContext.rotate(this.rotate * Math.PI / 180);
31382 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31384 var canvas = document.createElement("canvas");
31386 var context = canvas.getContext("2d");
31388 canvas.width = this.minWidth;
31389 canvas.height = this.minHeight;
31391 switch (this.rotate) {
31394 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31395 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31397 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31398 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31400 var targetWidth = this.minWidth - 2 * x;
31401 var targetHeight = this.minHeight - 2 * y;
31405 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31406 scale = targetWidth / width;
31409 if(x > 0 && y == 0){
31410 scale = targetHeight / height;
31413 if(x > 0 && y > 0){
31414 scale = targetWidth / width;
31416 if(width < height){
31417 scale = targetHeight / height;
31421 context.scale(scale, scale);
31423 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31424 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31426 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31427 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31429 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31434 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31435 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31437 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31438 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31440 var targetWidth = this.minWidth - 2 * x;
31441 var targetHeight = this.minHeight - 2 * y;
31445 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31446 scale = targetWidth / width;
31449 if(x > 0 && y == 0){
31450 scale = targetHeight / height;
31453 if(x > 0 && y > 0){
31454 scale = targetWidth / width;
31456 if(width < height){
31457 scale = targetHeight / height;
31461 context.scale(scale, scale);
31463 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31464 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31466 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31467 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31469 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31471 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31476 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31477 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31479 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31480 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31482 var targetWidth = this.minWidth - 2 * x;
31483 var targetHeight = this.minHeight - 2 * y;
31487 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31488 scale = targetWidth / width;
31491 if(x > 0 && y == 0){
31492 scale = targetHeight / height;
31495 if(x > 0 && y > 0){
31496 scale = targetWidth / width;
31498 if(width < height){
31499 scale = targetHeight / height;
31503 context.scale(scale, scale);
31505 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31506 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31508 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31509 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31511 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31512 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31514 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31519 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31520 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31522 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31523 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31525 var targetWidth = this.minWidth - 2 * x;
31526 var targetHeight = this.minHeight - 2 * y;
31530 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31531 scale = targetWidth / width;
31534 if(x > 0 && y == 0){
31535 scale = targetHeight / height;
31538 if(x > 0 && y > 0){
31539 scale = targetWidth / width;
31541 if(width < height){
31542 scale = targetHeight / height;
31546 context.scale(scale, scale);
31548 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31549 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31551 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31552 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31554 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31556 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31563 this.cropData = canvas.toDataURL(this.cropType);
31565 if(this.fireEvent('crop', this, this.cropData) !== false){
31566 this.process(this.file, this.cropData);
31573 setThumbBoxSize : function()
31577 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31578 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31579 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31581 this.minWidth = width;
31582 this.minHeight = height;
31584 if(this.rotate == 90 || this.rotate == 270){
31585 this.minWidth = height;
31586 this.minHeight = width;
31591 width = Math.ceil(this.minWidth * height / this.minHeight);
31593 if(this.minWidth > this.minHeight){
31595 height = Math.ceil(this.minHeight * width / this.minWidth);
31598 this.thumbEl.setStyle({
31599 width : width + 'px',
31600 height : height + 'px'
31607 setThumbBoxPosition : function()
31609 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31610 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31612 this.thumbEl.setLeft(x);
31613 this.thumbEl.setTop(y);
31617 baseRotateLevel : function()
31619 this.baseRotate = 1;
31622 typeof(this.exif) != 'undefined' &&
31623 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31624 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31626 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31629 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31633 baseScaleLevel : function()
31637 if(this.isDocument){
31639 if(this.baseRotate == 6 || this.baseRotate == 8){
31641 height = this.thumbEl.getHeight();
31642 this.baseScale = height / this.imageEl.OriginWidth;
31644 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31645 width = this.thumbEl.getWidth();
31646 this.baseScale = width / this.imageEl.OriginHeight;
31652 height = this.thumbEl.getHeight();
31653 this.baseScale = height / this.imageEl.OriginHeight;
31655 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31656 width = this.thumbEl.getWidth();
31657 this.baseScale = width / this.imageEl.OriginWidth;
31663 if(this.baseRotate == 6 || this.baseRotate == 8){
31665 width = this.thumbEl.getHeight();
31666 this.baseScale = width / this.imageEl.OriginHeight;
31668 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31669 height = this.thumbEl.getWidth();
31670 this.baseScale = height / this.imageEl.OriginHeight;
31673 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31674 height = this.thumbEl.getWidth();
31675 this.baseScale = height / this.imageEl.OriginHeight;
31677 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31678 width = this.thumbEl.getHeight();
31679 this.baseScale = width / this.imageEl.OriginWidth;
31686 width = this.thumbEl.getWidth();
31687 this.baseScale = width / this.imageEl.OriginWidth;
31689 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31690 height = this.thumbEl.getHeight();
31691 this.baseScale = height / this.imageEl.OriginHeight;
31694 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31696 height = this.thumbEl.getHeight();
31697 this.baseScale = height / this.imageEl.OriginHeight;
31699 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31700 width = this.thumbEl.getWidth();
31701 this.baseScale = width / this.imageEl.OriginWidth;
31709 getScaleLevel : function()
31711 return this.baseScale * Math.pow(1.1, this.scale);
31714 onTouchStart : function(e)
31716 if(!this.canvasLoaded){
31717 this.beforeSelectFile(e);
31721 var touches = e.browserEvent.touches;
31727 if(touches.length == 1){
31728 this.onMouseDown(e);
31732 if(touches.length != 2){
31738 for(var i = 0, finger; finger = touches[i]; i++){
31739 coords.push(finger.pageX, finger.pageY);
31742 var x = Math.pow(coords[0] - coords[2], 2);
31743 var y = Math.pow(coords[1] - coords[3], 2);
31745 this.startDistance = Math.sqrt(x + y);
31747 this.startScale = this.scale;
31749 this.pinching = true;
31750 this.dragable = false;
31754 onTouchMove : function(e)
31756 if(!this.pinching && !this.dragable){
31760 var touches = e.browserEvent.touches;
31767 this.onMouseMove(e);
31773 for(var i = 0, finger; finger = touches[i]; i++){
31774 coords.push(finger.pageX, finger.pageY);
31777 var x = Math.pow(coords[0] - coords[2], 2);
31778 var y = Math.pow(coords[1] - coords[3], 2);
31780 this.endDistance = Math.sqrt(x + y);
31782 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31784 if(!this.zoomable()){
31785 this.scale = this.startScale;
31793 onTouchEnd : function(e)
31795 this.pinching = false;
31796 this.dragable = false;
31800 process : function(file, crop)
31803 this.maskEl.mask(this.loadingText);
31806 this.xhr = new XMLHttpRequest();
31808 file.xhr = this.xhr;
31810 this.xhr.open(this.method, this.url, true);
31813 "Accept": "application/json",
31814 "Cache-Control": "no-cache",
31815 "X-Requested-With": "XMLHttpRequest"
31818 for (var headerName in headers) {
31819 var headerValue = headers[headerName];
31821 this.xhr.setRequestHeader(headerName, headerValue);
31827 this.xhr.onload = function()
31829 _this.xhrOnLoad(_this.xhr);
31832 this.xhr.onerror = function()
31834 _this.xhrOnError(_this.xhr);
31837 var formData = new FormData();
31839 formData.append('returnHTML', 'NO');
31842 formData.append('crop', crop);
31845 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31846 formData.append(this.paramName, file, file.name);
31849 if(typeof(file.filename) != 'undefined'){
31850 formData.append('filename', file.filename);
31853 if(typeof(file.mimetype) != 'undefined'){
31854 formData.append('mimetype', file.mimetype);
31857 if(this.fireEvent('arrange', this, formData) != false){
31858 this.xhr.send(formData);
31862 xhrOnLoad : function(xhr)
31865 this.maskEl.unmask();
31868 if (xhr.readyState !== 4) {
31869 this.fireEvent('exception', this, xhr);
31873 var response = Roo.decode(xhr.responseText);
31875 if(!response.success){
31876 this.fireEvent('exception', this, xhr);
31880 var response = Roo.decode(xhr.responseText);
31882 this.fireEvent('upload', this, response);
31886 xhrOnError : function()
31889 this.maskEl.unmask();
31892 Roo.log('xhr on error');
31894 var response = Roo.decode(xhr.responseText);
31900 prepare : function(file)
31903 this.maskEl.mask(this.loadingText);
31909 if(typeof(file) === 'string'){
31910 this.loadCanvas(file);
31914 if(!file || !this.urlAPI){
31919 this.cropType = file.type;
31923 if(this.fireEvent('prepare', this, this.file) != false){
31925 var reader = new FileReader();
31927 reader.onload = function (e) {
31928 if (e.target.error) {
31929 Roo.log(e.target.error);
31933 var buffer = e.target.result,
31934 dataView = new DataView(buffer),
31936 maxOffset = dataView.byteLength - 4,
31940 if (dataView.getUint16(0) === 0xffd8) {
31941 while (offset < maxOffset) {
31942 markerBytes = dataView.getUint16(offset);
31944 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31945 markerLength = dataView.getUint16(offset + 2) + 2;
31946 if (offset + markerLength > dataView.byteLength) {
31947 Roo.log('Invalid meta data: Invalid segment size.');
31951 if(markerBytes == 0xffe1){
31952 _this.parseExifData(
31959 offset += markerLength;
31969 var url = _this.urlAPI.createObjectURL(_this.file);
31971 _this.loadCanvas(url);
31976 reader.readAsArrayBuffer(this.file);
31982 parseExifData : function(dataView, offset, length)
31984 var tiffOffset = offset + 10,
31988 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31989 // No Exif data, might be XMP data instead
31993 // Check for the ASCII code for "Exif" (0x45786966):
31994 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31995 // No Exif data, might be XMP data instead
31998 if (tiffOffset + 8 > dataView.byteLength) {
31999 Roo.log('Invalid Exif data: Invalid segment size.');
32002 // Check for the two null bytes:
32003 if (dataView.getUint16(offset + 8) !== 0x0000) {
32004 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32007 // Check the byte alignment:
32008 switch (dataView.getUint16(tiffOffset)) {
32010 littleEndian = true;
32013 littleEndian = false;
32016 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32019 // Check for the TIFF tag marker (0x002A):
32020 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32021 Roo.log('Invalid Exif data: Missing TIFF marker.');
32024 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32025 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32027 this.parseExifTags(
32030 tiffOffset + dirOffset,
32035 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32040 if (dirOffset + 6 > dataView.byteLength) {
32041 Roo.log('Invalid Exif data: Invalid directory offset.');
32044 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32045 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32046 if (dirEndOffset + 4 > dataView.byteLength) {
32047 Roo.log('Invalid Exif data: Invalid directory size.');
32050 for (i = 0; i < tagsNumber; i += 1) {
32054 dirOffset + 2 + 12 * i, // tag offset
32058 // Return the offset to the next directory:
32059 return dataView.getUint32(dirEndOffset, littleEndian);
32062 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32064 var tag = dataView.getUint16(offset, littleEndian);
32066 this.exif[tag] = this.getExifValue(
32070 dataView.getUint16(offset + 2, littleEndian), // tag type
32071 dataView.getUint32(offset + 4, littleEndian), // tag length
32076 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32078 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32087 Roo.log('Invalid Exif data: Invalid tag type.');
32091 tagSize = tagType.size * length;
32092 // Determine if the value is contained in the dataOffset bytes,
32093 // or if the value at the dataOffset is a pointer to the actual data:
32094 dataOffset = tagSize > 4 ?
32095 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32096 if (dataOffset + tagSize > dataView.byteLength) {
32097 Roo.log('Invalid Exif data: Invalid data offset.');
32100 if (length === 1) {
32101 return tagType.getValue(dataView, dataOffset, littleEndian);
32104 for (i = 0; i < length; i += 1) {
32105 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32108 if (tagType.ascii) {
32110 // Concatenate the chars:
32111 for (i = 0; i < values.length; i += 1) {
32113 // Ignore the terminating NULL byte(s):
32114 if (c === '\u0000') {
32126 Roo.apply(Roo.bootstrap.UploadCropbox, {
32128 'Orientation': 0x0112
32132 1: 0, //'top-left',
32134 3: 180, //'bottom-right',
32135 // 4: 'bottom-left',
32137 6: 90, //'right-top',
32138 // 7: 'right-bottom',
32139 8: 270 //'left-bottom'
32143 // byte, 8-bit unsigned int:
32145 getValue: function (dataView, dataOffset) {
32146 return dataView.getUint8(dataOffset);
32150 // ascii, 8-bit byte:
32152 getValue: function (dataView, dataOffset) {
32153 return String.fromCharCode(dataView.getUint8(dataOffset));
32158 // short, 16 bit int:
32160 getValue: function (dataView, dataOffset, littleEndian) {
32161 return dataView.getUint16(dataOffset, littleEndian);
32165 // long, 32 bit int:
32167 getValue: function (dataView, dataOffset, littleEndian) {
32168 return dataView.getUint32(dataOffset, littleEndian);
32172 // rational = two long values, first is numerator, second is denominator:
32174 getValue: function (dataView, dataOffset, littleEndian) {
32175 return dataView.getUint32(dataOffset, littleEndian) /
32176 dataView.getUint32(dataOffset + 4, littleEndian);
32180 // slong, 32 bit signed int:
32182 getValue: function (dataView, dataOffset, littleEndian) {
32183 return dataView.getInt32(dataOffset, littleEndian);
32187 // srational, two slongs, first is numerator, second is denominator:
32189 getValue: function (dataView, dataOffset, littleEndian) {
32190 return dataView.getInt32(dataOffset, littleEndian) /
32191 dataView.getInt32(dataOffset + 4, littleEndian);
32201 cls : 'btn-group roo-upload-cropbox-rotate-left',
32202 action : 'rotate-left',
32206 cls : 'btn btn-default',
32207 html : '<i class="fa fa-undo"></i>'
32213 cls : 'btn-group roo-upload-cropbox-picture',
32214 action : 'picture',
32218 cls : 'btn btn-default',
32219 html : '<i class="fa fa-picture-o"></i>'
32225 cls : 'btn-group roo-upload-cropbox-rotate-right',
32226 action : 'rotate-right',
32230 cls : 'btn btn-default',
32231 html : '<i class="fa fa-repeat"></i>'
32239 cls : 'btn-group roo-upload-cropbox-rotate-left',
32240 action : 'rotate-left',
32244 cls : 'btn btn-default',
32245 html : '<i class="fa fa-undo"></i>'
32251 cls : 'btn-group roo-upload-cropbox-download',
32252 action : 'download',
32256 cls : 'btn btn-default',
32257 html : '<i class="fa fa-download"></i>'
32263 cls : 'btn-group roo-upload-cropbox-crop',
32268 cls : 'btn btn-default',
32269 html : '<i class="fa fa-crop"></i>'
32275 cls : 'btn-group roo-upload-cropbox-trash',
32280 cls : 'btn btn-default',
32281 html : '<i class="fa fa-trash"></i>'
32287 cls : 'btn-group roo-upload-cropbox-rotate-right',
32288 action : 'rotate-right',
32292 cls : 'btn btn-default',
32293 html : '<i class="fa fa-repeat"></i>'
32301 cls : 'btn-group roo-upload-cropbox-rotate-left',
32302 action : 'rotate-left',
32306 cls : 'btn btn-default',
32307 html : '<i class="fa fa-undo"></i>'
32313 cls : 'btn-group roo-upload-cropbox-rotate-right',
32314 action : 'rotate-right',
32318 cls : 'btn btn-default',
32319 html : '<i class="fa fa-repeat"></i>'
32332 * @class Roo.bootstrap.DocumentManager
32333 * @extends Roo.bootstrap.Component
32334 * Bootstrap DocumentManager class
32335 * @cfg {String} paramName default 'imageUpload'
32336 * @cfg {String} toolTipName default 'filename'
32337 * @cfg {String} method default POST
32338 * @cfg {String} url action url
32339 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32340 * @cfg {Boolean} multiple multiple upload default true
32341 * @cfg {Number} thumbSize default 300
32342 * @cfg {String} fieldLabel
32343 * @cfg {Number} labelWidth default 4
32344 * @cfg {String} labelAlign (left|top) default left
32345 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32346 * @cfg {Number} labellg set the width of label (1-12)
32347 * @cfg {Number} labelmd set the width of label (1-12)
32348 * @cfg {Number} labelsm set the width of label (1-12)
32349 * @cfg {Number} labelxs set the width of label (1-12)
32352 * Create a new DocumentManager
32353 * @param {Object} config The config object
32356 Roo.bootstrap.DocumentManager = function(config){
32357 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32360 this.delegates = [];
32365 * Fire when initial the DocumentManager
32366 * @param {Roo.bootstrap.DocumentManager} this
32371 * inspect selected file
32372 * @param {Roo.bootstrap.DocumentManager} this
32373 * @param {File} file
32378 * Fire when xhr load exception
32379 * @param {Roo.bootstrap.DocumentManager} this
32380 * @param {XMLHttpRequest} xhr
32382 "exception" : true,
32384 * @event afterupload
32385 * Fire when xhr load exception
32386 * @param {Roo.bootstrap.DocumentManager} this
32387 * @param {XMLHttpRequest} xhr
32389 "afterupload" : true,
32392 * prepare the form data
32393 * @param {Roo.bootstrap.DocumentManager} this
32394 * @param {Object} formData
32399 * Fire when remove the file
32400 * @param {Roo.bootstrap.DocumentManager} this
32401 * @param {Object} file
32406 * Fire after refresh the file
32407 * @param {Roo.bootstrap.DocumentManager} this
32412 * Fire after click the image
32413 * @param {Roo.bootstrap.DocumentManager} this
32414 * @param {Object} file
32419 * Fire when upload a image and editable set to true
32420 * @param {Roo.bootstrap.DocumentManager} this
32421 * @param {Object} file
32425 * @event beforeselectfile
32426 * Fire before select file
32427 * @param {Roo.bootstrap.DocumentManager} this
32429 "beforeselectfile" : true,
32432 * Fire before process file
32433 * @param {Roo.bootstrap.DocumentManager} this
32434 * @param {Object} file
32438 * @event previewrendered
32439 * Fire when preview rendered
32440 * @param {Roo.bootstrap.DocumentManager} this
32441 * @param {Object} file
32443 "previewrendered" : true,
32446 "previewResize" : true
32451 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32460 paramName : 'imageUpload',
32461 toolTipName : 'filename',
32464 labelAlign : 'left',
32474 getAutoCreate : function()
32476 var managerWidget = {
32478 cls : 'roo-document-manager',
32482 cls : 'roo-document-manager-selector',
32487 cls : 'roo-document-manager-uploader',
32491 cls : 'roo-document-manager-upload-btn',
32492 html : '<i class="fa fa-plus"></i>'
32503 cls : 'column col-md-12',
32508 if(this.fieldLabel.length){
32513 cls : 'column col-md-12',
32514 html : this.fieldLabel
32518 cls : 'column col-md-12',
32523 if(this.labelAlign == 'left'){
32528 html : this.fieldLabel
32537 if(this.labelWidth > 12){
32538 content[0].style = "width: " + this.labelWidth + 'px';
32541 if(this.labelWidth < 13 && this.labelmd == 0){
32542 this.labelmd = this.labelWidth;
32545 if(this.labellg > 0){
32546 content[0].cls += ' col-lg-' + this.labellg;
32547 content[1].cls += ' col-lg-' + (12 - this.labellg);
32550 if(this.labelmd > 0){
32551 content[0].cls += ' col-md-' + this.labelmd;
32552 content[1].cls += ' col-md-' + (12 - this.labelmd);
32555 if(this.labelsm > 0){
32556 content[0].cls += ' col-sm-' + this.labelsm;
32557 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32560 if(this.labelxs > 0){
32561 content[0].cls += ' col-xs-' + this.labelxs;
32562 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32570 cls : 'row clearfix',
32578 initEvents : function()
32580 this.managerEl = this.el.select('.roo-document-manager', true).first();
32581 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32583 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32584 this.selectorEl.hide();
32587 this.selectorEl.attr('multiple', 'multiple');
32590 this.selectorEl.on('change', this.onFileSelected, this);
32592 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32593 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32595 this.uploader.on('click', this.onUploaderClick, this);
32597 this.renderProgressDialog();
32601 window.addEventListener("resize", function() { _this.refresh(); } );
32603 this.fireEvent('initial', this);
32606 renderProgressDialog : function()
32610 this.progressDialog = new Roo.bootstrap.Modal({
32611 cls : 'roo-document-manager-progress-dialog',
32612 allow_close : false,
32623 btnclick : function() {
32624 _this.uploadCancel();
32630 this.progressDialog.render(Roo.get(document.body));
32632 this.progress = new Roo.bootstrap.Progress({
32633 cls : 'roo-document-manager-progress',
32638 this.progress.render(this.progressDialog.getChildContainer());
32640 this.progressBar = new Roo.bootstrap.ProgressBar({
32641 cls : 'roo-document-manager-progress-bar',
32644 aria_valuemax : 12,
32648 this.progressBar.render(this.progress.getChildContainer());
32651 onUploaderClick : function(e)
32653 e.preventDefault();
32655 if(this.fireEvent('beforeselectfile', this) != false){
32656 this.selectorEl.dom.click();
32661 onFileSelected : function(e)
32663 e.preventDefault();
32665 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32669 Roo.each(this.selectorEl.dom.files, function(file){
32670 if(this.fireEvent('inspect', this, file) != false){
32671 this.files.push(file);
32681 this.selectorEl.dom.value = '';
32683 if(!this.files || !this.files.length){
32687 if(this.boxes > 0 && this.files.length > this.boxes){
32688 this.files = this.files.slice(0, this.boxes);
32691 this.uploader.show();
32693 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32694 this.uploader.hide();
32703 Roo.each(this.files, function(file){
32705 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32706 var f = this.renderPreview(file);
32711 if(file.type.indexOf('image') != -1){
32712 this.delegates.push(
32714 _this.process(file);
32715 }).createDelegate(this)
32723 _this.process(file);
32724 }).createDelegate(this)
32729 this.files = files;
32731 this.delegates = this.delegates.concat(docs);
32733 if(!this.delegates.length){
32738 this.progressBar.aria_valuemax = this.delegates.length;
32745 arrange : function()
32747 if(!this.delegates.length){
32748 this.progressDialog.hide();
32753 var delegate = this.delegates.shift();
32755 this.progressDialog.show();
32757 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32759 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32764 refresh : function()
32766 this.uploader.show();
32768 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32769 this.uploader.hide();
32772 Roo.isTouch ? this.closable(false) : this.closable(true);
32774 this.fireEvent('refresh', this);
32777 onRemove : function(e, el, o)
32779 e.preventDefault();
32781 this.fireEvent('remove', this, o);
32785 remove : function(o)
32789 Roo.each(this.files, function(file){
32790 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32799 this.files = files;
32806 Roo.each(this.files, function(file){
32811 file.target.remove();
32820 onClick : function(e, el, o)
32822 e.preventDefault();
32824 this.fireEvent('click', this, o);
32828 closable : function(closable)
32830 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32832 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32844 xhrOnLoad : function(xhr)
32846 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32850 if (xhr.readyState !== 4) {
32852 this.fireEvent('exception', this, xhr);
32856 var response = Roo.decode(xhr.responseText);
32858 if(!response.success){
32860 this.fireEvent('exception', this, xhr);
32864 var file = this.renderPreview(response.data);
32866 this.files.push(file);
32870 this.fireEvent('afterupload', this, xhr);
32874 xhrOnError : function(xhr)
32876 Roo.log('xhr on error');
32878 var response = Roo.decode(xhr.responseText);
32885 process : function(file)
32887 if(this.fireEvent('process', this, file) !== false){
32888 if(this.editable && file.type.indexOf('image') != -1){
32889 this.fireEvent('edit', this, file);
32893 this.uploadStart(file, false);
32900 uploadStart : function(file, crop)
32902 this.xhr = new XMLHttpRequest();
32904 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32909 file.xhr = this.xhr;
32911 this.managerEl.createChild({
32913 cls : 'roo-document-manager-loading',
32917 tooltip : file.name,
32918 cls : 'roo-document-manager-thumb',
32919 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32925 this.xhr.open(this.method, this.url, true);
32928 "Accept": "application/json",
32929 "Cache-Control": "no-cache",
32930 "X-Requested-With": "XMLHttpRequest"
32933 for (var headerName in headers) {
32934 var headerValue = headers[headerName];
32936 this.xhr.setRequestHeader(headerName, headerValue);
32942 this.xhr.onload = function()
32944 _this.xhrOnLoad(_this.xhr);
32947 this.xhr.onerror = function()
32949 _this.xhrOnError(_this.xhr);
32952 var formData = new FormData();
32954 formData.append('returnHTML', 'NO');
32957 formData.append('crop', crop);
32960 formData.append(this.paramName, file, file.name);
32967 if(this.fireEvent('prepare', this, formData, options) != false){
32969 if(options.manually){
32973 this.xhr.send(formData);
32977 this.uploadCancel();
32980 uploadCancel : function()
32986 this.delegates = [];
32988 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32995 renderPreview : function(file)
32997 if(typeof(file.target) != 'undefined' && file.target){
33001 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33003 var previewEl = this.managerEl.createChild({
33005 cls : 'roo-document-manager-preview',
33009 tooltip : file[this.toolTipName],
33010 cls : 'roo-document-manager-thumb',
33011 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33016 html : '<i class="fa fa-times-circle"></i>'
33021 var close = previewEl.select('button.close', true).first();
33023 close.on('click', this.onRemove, this, file);
33025 file.target = previewEl;
33027 var image = previewEl.select('img', true).first();
33031 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33033 image.on('click', this.onClick, this, file);
33035 this.fireEvent('previewrendered', this, file);
33041 onPreviewLoad : function(file, image)
33043 if(typeof(file.target) == 'undefined' || !file.target){
33047 var width = image.dom.naturalWidth || image.dom.width;
33048 var height = image.dom.naturalHeight || image.dom.height;
33050 if(!this.previewResize) {
33054 if(width > height){
33055 file.target.addClass('wide');
33059 file.target.addClass('tall');
33064 uploadFromSource : function(file, crop)
33066 this.xhr = new XMLHttpRequest();
33068 this.managerEl.createChild({
33070 cls : 'roo-document-manager-loading',
33074 tooltip : file.name,
33075 cls : 'roo-document-manager-thumb',
33076 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33082 this.xhr.open(this.method, this.url, true);
33085 "Accept": "application/json",
33086 "Cache-Control": "no-cache",
33087 "X-Requested-With": "XMLHttpRequest"
33090 for (var headerName in headers) {
33091 var headerValue = headers[headerName];
33093 this.xhr.setRequestHeader(headerName, headerValue);
33099 this.xhr.onload = function()
33101 _this.xhrOnLoad(_this.xhr);
33104 this.xhr.onerror = function()
33106 _this.xhrOnError(_this.xhr);
33109 var formData = new FormData();
33111 formData.append('returnHTML', 'NO');
33113 formData.append('crop', crop);
33115 if(typeof(file.filename) != 'undefined'){
33116 formData.append('filename', file.filename);
33119 if(typeof(file.mimetype) != 'undefined'){
33120 formData.append('mimetype', file.mimetype);
33125 if(this.fireEvent('prepare', this, formData) != false){
33126 this.xhr.send(formData);
33136 * @class Roo.bootstrap.DocumentViewer
33137 * @extends Roo.bootstrap.Component
33138 * Bootstrap DocumentViewer class
33139 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33140 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33143 * Create a new DocumentViewer
33144 * @param {Object} config The config object
33147 Roo.bootstrap.DocumentViewer = function(config){
33148 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33153 * Fire after initEvent
33154 * @param {Roo.bootstrap.DocumentViewer} this
33160 * @param {Roo.bootstrap.DocumentViewer} this
33165 * Fire after download button
33166 * @param {Roo.bootstrap.DocumentViewer} this
33171 * Fire after trash button
33172 * @param {Roo.bootstrap.DocumentViewer} this
33179 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33181 showDownload : true,
33185 getAutoCreate : function()
33189 cls : 'roo-document-viewer',
33193 cls : 'roo-document-viewer-body',
33197 cls : 'roo-document-viewer-thumb',
33201 cls : 'roo-document-viewer-image'
33209 cls : 'roo-document-viewer-footer',
33212 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33216 cls : 'btn-group roo-document-viewer-download',
33220 cls : 'btn btn-default',
33221 html : '<i class="fa fa-download"></i>'
33227 cls : 'btn-group roo-document-viewer-trash',
33231 cls : 'btn btn-default',
33232 html : '<i class="fa fa-trash"></i>'
33245 initEvents : function()
33247 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33248 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33250 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33251 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33253 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33254 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33256 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33257 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33259 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33260 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33262 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33263 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33265 this.bodyEl.on('click', this.onClick, this);
33266 this.downloadBtn.on('click', this.onDownload, this);
33267 this.trashBtn.on('click', this.onTrash, this);
33269 this.downloadBtn.hide();
33270 this.trashBtn.hide();
33272 if(this.showDownload){
33273 this.downloadBtn.show();
33276 if(this.showTrash){
33277 this.trashBtn.show();
33280 if(!this.showDownload && !this.showTrash) {
33281 this.footerEl.hide();
33286 initial : function()
33288 this.fireEvent('initial', this);
33292 onClick : function(e)
33294 e.preventDefault();
33296 this.fireEvent('click', this);
33299 onDownload : function(e)
33301 e.preventDefault();
33303 this.fireEvent('download', this);
33306 onTrash : function(e)
33308 e.preventDefault();
33310 this.fireEvent('trash', this);
33322 * @class Roo.bootstrap.NavProgressBar
33323 * @extends Roo.bootstrap.Component
33324 * Bootstrap NavProgressBar class
33327 * Create a new nav progress bar
33328 * @param {Object} config The config object
33331 Roo.bootstrap.NavProgressBar = function(config){
33332 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33334 this.bullets = this.bullets || [];
33336 // Roo.bootstrap.NavProgressBar.register(this);
33340 * Fires when the active item changes
33341 * @param {Roo.bootstrap.NavProgressBar} this
33342 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33343 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33350 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33355 getAutoCreate : function()
33357 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33361 cls : 'roo-navigation-bar-group',
33365 cls : 'roo-navigation-top-bar'
33369 cls : 'roo-navigation-bullets-bar',
33373 cls : 'roo-navigation-bar'
33380 cls : 'roo-navigation-bottom-bar'
33390 initEvents: function()
33395 onRender : function(ct, position)
33397 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33399 if(this.bullets.length){
33400 Roo.each(this.bullets, function(b){
33409 addItem : function(cfg)
33411 var item = new Roo.bootstrap.NavProgressItem(cfg);
33413 item.parentId = this.id;
33414 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33417 var top = new Roo.bootstrap.Element({
33419 cls : 'roo-navigation-bar-text'
33422 var bottom = new Roo.bootstrap.Element({
33424 cls : 'roo-navigation-bar-text'
33427 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33428 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33430 var topText = new Roo.bootstrap.Element({
33432 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33435 var bottomText = new Roo.bootstrap.Element({
33437 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33440 topText.onRender(top.el, null);
33441 bottomText.onRender(bottom.el, null);
33444 item.bottomEl = bottom;
33447 this.barItems.push(item);
33452 getActive : function()
33454 var active = false;
33456 Roo.each(this.barItems, function(v){
33458 if (!v.isActive()) {
33470 setActiveItem : function(item)
33474 Roo.each(this.barItems, function(v){
33475 if (v.rid == item.rid) {
33479 if (v.isActive()) {
33480 v.setActive(false);
33485 item.setActive(true);
33487 this.fireEvent('changed', this, item, prev);
33490 getBarItem: function(rid)
33494 Roo.each(this.barItems, function(e) {
33495 if (e.rid != rid) {
33506 indexOfItem : function(item)
33510 Roo.each(this.barItems, function(v, i){
33512 if (v.rid != item.rid) {
33523 setActiveNext : function()
33525 var i = this.indexOfItem(this.getActive());
33527 if (i > this.barItems.length) {
33531 this.setActiveItem(this.barItems[i+1]);
33534 setActivePrev : function()
33536 var i = this.indexOfItem(this.getActive());
33542 this.setActiveItem(this.barItems[i-1]);
33545 format : function()
33547 if(!this.barItems.length){
33551 var width = 100 / this.barItems.length;
33553 Roo.each(this.barItems, function(i){
33554 i.el.setStyle('width', width + '%');
33555 i.topEl.el.setStyle('width', width + '%');
33556 i.bottomEl.el.setStyle('width', width + '%');
33565 * Nav Progress Item
33570 * @class Roo.bootstrap.NavProgressItem
33571 * @extends Roo.bootstrap.Component
33572 * Bootstrap NavProgressItem class
33573 * @cfg {String} rid the reference id
33574 * @cfg {Boolean} active (true|false) Is item active default false
33575 * @cfg {Boolean} disabled (true|false) Is item active default false
33576 * @cfg {String} html
33577 * @cfg {String} position (top|bottom) text position default bottom
33578 * @cfg {String} icon show icon instead of number
33581 * Create a new NavProgressItem
33582 * @param {Object} config The config object
33584 Roo.bootstrap.NavProgressItem = function(config){
33585 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33590 * The raw click event for the entire grid.
33591 * @param {Roo.bootstrap.NavProgressItem} this
33592 * @param {Roo.EventObject} e
33599 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33605 position : 'bottom',
33608 getAutoCreate : function()
33610 var iconCls = 'roo-navigation-bar-item-icon';
33612 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33616 cls: 'roo-navigation-bar-item',
33626 cfg.cls += ' active';
33629 cfg.cls += ' disabled';
33635 disable : function()
33637 this.setDisabled(true);
33640 enable : function()
33642 this.setDisabled(false);
33645 initEvents: function()
33647 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33649 this.iconEl.on('click', this.onClick, this);
33652 onClick : function(e)
33654 e.preventDefault();
33660 if(this.fireEvent('click', this, e) === false){
33664 this.parent().setActiveItem(this);
33667 isActive: function ()
33669 return this.active;
33672 setActive : function(state)
33674 if(this.active == state){
33678 this.active = state;
33681 this.el.addClass('active');
33685 this.el.removeClass('active');
33690 setDisabled : function(state)
33692 if(this.disabled == state){
33696 this.disabled = state;
33699 this.el.addClass('disabled');
33703 this.el.removeClass('disabled');
33706 tooltipEl : function()
33708 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33721 * @class Roo.bootstrap.FieldLabel
33722 * @extends Roo.bootstrap.Component
33723 * Bootstrap FieldLabel class
33724 * @cfg {String} html contents of the element
33725 * @cfg {String} tag tag of the element default label
33726 * @cfg {String} cls class of the element
33727 * @cfg {String} target label target
33728 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33729 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33730 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33731 * @cfg {String} iconTooltip default "This field is required"
33732 * @cfg {String} indicatorpos (left|right) default left
33735 * Create a new FieldLabel
33736 * @param {Object} config The config object
33739 Roo.bootstrap.FieldLabel = function(config){
33740 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33745 * Fires after the field has been marked as invalid.
33746 * @param {Roo.form.FieldLabel} this
33747 * @param {String} msg The validation message
33752 * Fires after the field has been validated with no errors.
33753 * @param {Roo.form.FieldLabel} this
33759 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33766 invalidClass : 'has-warning',
33767 validClass : 'has-success',
33768 iconTooltip : 'This field is required',
33769 indicatorpos : 'left',
33771 getAutoCreate : function(){
33774 if (!this.allowBlank) {
33780 cls : 'roo-bootstrap-field-label ' + this.cls,
33785 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33786 tooltip : this.iconTooltip
33795 if(this.indicatorpos == 'right'){
33798 cls : 'roo-bootstrap-field-label ' + this.cls,
33807 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33808 tooltip : this.iconTooltip
33817 initEvents: function()
33819 Roo.bootstrap.Element.superclass.initEvents.call(this);
33821 this.indicator = this.indicatorEl();
33823 if(this.indicator){
33824 this.indicator.removeClass('visible');
33825 this.indicator.addClass('invisible');
33828 Roo.bootstrap.FieldLabel.register(this);
33831 indicatorEl : function()
33833 var indicator = this.el.select('i.roo-required-indicator',true).first();
33844 * Mark this field as valid
33846 markValid : function()
33848 if(this.indicator){
33849 this.indicator.removeClass('visible');
33850 this.indicator.addClass('invisible');
33852 if (Roo.bootstrap.version == 3) {
33853 this.el.removeClass(this.invalidClass);
33854 this.el.addClass(this.validClass);
33856 this.el.removeClass('is-invalid');
33857 this.el.addClass('is-valid');
33861 this.fireEvent('valid', this);
33865 * Mark this field as invalid
33866 * @param {String} msg The validation message
33868 markInvalid : function(msg)
33870 if(this.indicator){
33871 this.indicator.removeClass('invisible');
33872 this.indicator.addClass('visible');
33874 if (Roo.bootstrap.version == 3) {
33875 this.el.removeClass(this.validClass);
33876 this.el.addClass(this.invalidClass);
33878 this.el.removeClass('is-valid');
33879 this.el.addClass('is-invalid');
33883 this.fireEvent('invalid', this, msg);
33889 Roo.apply(Roo.bootstrap.FieldLabel, {
33894 * register a FieldLabel Group
33895 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33897 register : function(label)
33899 if(this.groups.hasOwnProperty(label.target)){
33903 this.groups[label.target] = label;
33907 * fetch a FieldLabel Group based on the target
33908 * @param {string} target
33909 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33911 get: function(target) {
33912 if (typeof(this.groups[target]) == 'undefined') {
33916 return this.groups[target] ;
33925 * page DateSplitField.
33931 * @class Roo.bootstrap.DateSplitField
33932 * @extends Roo.bootstrap.Component
33933 * Bootstrap DateSplitField class
33934 * @cfg {string} fieldLabel - the label associated
33935 * @cfg {Number} labelWidth set the width of label (0-12)
33936 * @cfg {String} labelAlign (top|left)
33937 * @cfg {Boolean} dayAllowBlank (true|false) default false
33938 * @cfg {Boolean} monthAllowBlank (true|false) default false
33939 * @cfg {Boolean} yearAllowBlank (true|false) default false
33940 * @cfg {string} dayPlaceholder
33941 * @cfg {string} monthPlaceholder
33942 * @cfg {string} yearPlaceholder
33943 * @cfg {string} dayFormat default 'd'
33944 * @cfg {string} monthFormat default 'm'
33945 * @cfg {string} yearFormat default 'Y'
33946 * @cfg {Number} labellg set the width of label (1-12)
33947 * @cfg {Number} labelmd set the width of label (1-12)
33948 * @cfg {Number} labelsm set the width of label (1-12)
33949 * @cfg {Number} labelxs set the width of label (1-12)
33953 * Create a new DateSplitField
33954 * @param {Object} config The config object
33957 Roo.bootstrap.DateSplitField = function(config){
33958 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33964 * getting the data of years
33965 * @param {Roo.bootstrap.DateSplitField} this
33966 * @param {Object} years
33971 * getting the data of days
33972 * @param {Roo.bootstrap.DateSplitField} this
33973 * @param {Object} days
33978 * Fires after the field has been marked as invalid.
33979 * @param {Roo.form.Field} this
33980 * @param {String} msg The validation message
33985 * Fires after the field has been validated with no errors.
33986 * @param {Roo.form.Field} this
33992 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33995 labelAlign : 'top',
33997 dayAllowBlank : false,
33998 monthAllowBlank : false,
33999 yearAllowBlank : false,
34000 dayPlaceholder : '',
34001 monthPlaceholder : '',
34002 yearPlaceholder : '',
34006 isFormField : true,
34012 getAutoCreate : function()
34016 cls : 'row roo-date-split-field-group',
34021 cls : 'form-hidden-field roo-date-split-field-group-value',
34027 var labelCls = 'col-md-12';
34028 var contentCls = 'col-md-4';
34030 if(this.fieldLabel){
34034 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34038 html : this.fieldLabel
34043 if(this.labelAlign == 'left'){
34045 if(this.labelWidth > 12){
34046 label.style = "width: " + this.labelWidth + 'px';
34049 if(this.labelWidth < 13 && this.labelmd == 0){
34050 this.labelmd = this.labelWidth;
34053 if(this.labellg > 0){
34054 labelCls = ' col-lg-' + this.labellg;
34055 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34058 if(this.labelmd > 0){
34059 labelCls = ' col-md-' + this.labelmd;
34060 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34063 if(this.labelsm > 0){
34064 labelCls = ' col-sm-' + this.labelsm;
34065 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34068 if(this.labelxs > 0){
34069 labelCls = ' col-xs-' + this.labelxs;
34070 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34074 label.cls += ' ' + labelCls;
34076 cfg.cn.push(label);
34079 Roo.each(['day', 'month', 'year'], function(t){
34082 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34089 inputEl: function ()
34091 return this.el.select('.roo-date-split-field-group-value', true).first();
34094 onRender : function(ct, position)
34098 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34100 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34102 this.dayField = new Roo.bootstrap.ComboBox({
34103 allowBlank : this.dayAllowBlank,
34104 alwaysQuery : true,
34105 displayField : 'value',
34108 forceSelection : true,
34110 placeholder : this.dayPlaceholder,
34111 selectOnFocus : true,
34112 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34113 triggerAction : 'all',
34115 valueField : 'value',
34116 store : new Roo.data.SimpleStore({
34117 data : (function() {
34119 _this.fireEvent('days', _this, days);
34122 fields : [ 'value' ]
34125 select : function (_self, record, index)
34127 _this.setValue(_this.getValue());
34132 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34134 this.monthField = new Roo.bootstrap.MonthField({
34135 after : '<i class=\"fa fa-calendar\"></i>',
34136 allowBlank : this.monthAllowBlank,
34137 placeholder : this.monthPlaceholder,
34140 render : function (_self)
34142 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34143 e.preventDefault();
34147 select : function (_self, oldvalue, newvalue)
34149 _this.setValue(_this.getValue());
34154 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34156 this.yearField = new Roo.bootstrap.ComboBox({
34157 allowBlank : this.yearAllowBlank,
34158 alwaysQuery : true,
34159 displayField : 'value',
34162 forceSelection : true,
34164 placeholder : this.yearPlaceholder,
34165 selectOnFocus : true,
34166 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34167 triggerAction : 'all',
34169 valueField : 'value',
34170 store : new Roo.data.SimpleStore({
34171 data : (function() {
34173 _this.fireEvent('years', _this, years);
34176 fields : [ 'value' ]
34179 select : function (_self, record, index)
34181 _this.setValue(_this.getValue());
34186 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34189 setValue : function(v, format)
34191 this.inputEl.dom.value = v;
34193 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34195 var d = Date.parseDate(v, f);
34202 this.setDay(d.format(this.dayFormat));
34203 this.setMonth(d.format(this.monthFormat));
34204 this.setYear(d.format(this.yearFormat));
34211 setDay : function(v)
34213 this.dayField.setValue(v);
34214 this.inputEl.dom.value = this.getValue();
34219 setMonth : function(v)
34221 this.monthField.setValue(v, true);
34222 this.inputEl.dom.value = this.getValue();
34227 setYear : function(v)
34229 this.yearField.setValue(v);
34230 this.inputEl.dom.value = this.getValue();
34235 getDay : function()
34237 return this.dayField.getValue();
34240 getMonth : function()
34242 return this.monthField.getValue();
34245 getYear : function()
34247 return this.yearField.getValue();
34250 getValue : function()
34252 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34254 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34264 this.inputEl.dom.value = '';
34269 validate : function()
34271 var d = this.dayField.validate();
34272 var m = this.monthField.validate();
34273 var y = this.yearField.validate();
34278 (!this.dayAllowBlank && !d) ||
34279 (!this.monthAllowBlank && !m) ||
34280 (!this.yearAllowBlank && !y)
34285 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34294 this.markInvalid();
34299 markValid : function()
34302 var label = this.el.select('label', true).first();
34303 var icon = this.el.select('i.fa-star', true).first();
34309 this.fireEvent('valid', this);
34313 * Mark this field as invalid
34314 * @param {String} msg The validation message
34316 markInvalid : function(msg)
34319 var label = this.el.select('label', true).first();
34320 var icon = this.el.select('i.fa-star', true).first();
34322 if(label && !icon){
34323 this.el.select('.roo-date-split-field-label', true).createChild({
34325 cls : 'text-danger fa fa-lg fa-star',
34326 tooltip : 'This field is required',
34327 style : 'margin-right:5px;'
34331 this.fireEvent('invalid', this, msg);
34334 clearInvalid : function()
34336 var label = this.el.select('label', true).first();
34337 var icon = this.el.select('i.fa-star', true).first();
34343 this.fireEvent('valid', this);
34346 getName: function()
34356 * http://masonry.desandro.com
34358 * The idea is to render all the bricks based on vertical width...
34360 * The original code extends 'outlayer' - we might need to use that....
34366 * @class Roo.bootstrap.LayoutMasonry
34367 * @extends Roo.bootstrap.Component
34368 * Bootstrap Layout Masonry class
34371 * Create a new Element
34372 * @param {Object} config The config object
34375 Roo.bootstrap.LayoutMasonry = function(config){
34377 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34381 Roo.bootstrap.LayoutMasonry.register(this);
34387 * Fire after layout the items
34388 * @param {Roo.bootstrap.LayoutMasonry} this
34389 * @param {Roo.EventObject} e
34396 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34399 * @cfg {Boolean} isLayoutInstant = no animation?
34401 isLayoutInstant : false, // needed?
34404 * @cfg {Number} boxWidth width of the columns
34409 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34414 * @cfg {Number} padWidth padding below box..
34419 * @cfg {Number} gutter gutter width..
34424 * @cfg {Number} maxCols maximum number of columns
34430 * @cfg {Boolean} isAutoInitial defalut true
34432 isAutoInitial : true,
34437 * @cfg {Boolean} isHorizontal defalut false
34439 isHorizontal : false,
34441 currentSize : null,
34447 bricks: null, //CompositeElement
34451 _isLayoutInited : false,
34453 // isAlternative : false, // only use for vertical layout...
34456 * @cfg {Number} alternativePadWidth padding below box..
34458 alternativePadWidth : 50,
34460 selectedBrick : [],
34462 getAutoCreate : function(){
34464 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34468 cls: 'blog-masonary-wrapper ' + this.cls,
34470 cls : 'mas-boxes masonary'
34477 getChildContainer: function( )
34479 if (this.boxesEl) {
34480 return this.boxesEl;
34483 this.boxesEl = this.el.select('.mas-boxes').first();
34485 return this.boxesEl;
34489 initEvents : function()
34493 if(this.isAutoInitial){
34494 Roo.log('hook children rendered');
34495 this.on('childrenrendered', function() {
34496 Roo.log('children rendered');
34502 initial : function()
34504 this.selectedBrick = [];
34506 this.currentSize = this.el.getBox(true);
34508 Roo.EventManager.onWindowResize(this.resize, this);
34510 if(!this.isAutoInitial){
34518 //this.layout.defer(500,this);
34522 resize : function()
34524 var cs = this.el.getBox(true);
34527 this.currentSize.width == cs.width &&
34528 this.currentSize.x == cs.x &&
34529 this.currentSize.height == cs.height &&
34530 this.currentSize.y == cs.y
34532 Roo.log("no change in with or X or Y");
34536 this.currentSize = cs;
34542 layout : function()
34544 this._resetLayout();
34546 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34548 this.layoutItems( isInstant );
34550 this._isLayoutInited = true;
34552 this.fireEvent('layout', this);
34556 _resetLayout : function()
34558 if(this.isHorizontal){
34559 this.horizontalMeasureColumns();
34563 this.verticalMeasureColumns();
34567 verticalMeasureColumns : function()
34569 this.getContainerWidth();
34571 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34572 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34576 var boxWidth = this.boxWidth + this.padWidth;
34578 if(this.containerWidth < this.boxWidth){
34579 boxWidth = this.containerWidth
34582 var containerWidth = this.containerWidth;
34584 var cols = Math.floor(containerWidth / boxWidth);
34586 this.cols = Math.max( cols, 1 );
34588 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34590 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34592 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34594 this.colWidth = boxWidth + avail - this.padWidth;
34596 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34597 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34600 horizontalMeasureColumns : function()
34602 this.getContainerWidth();
34604 var boxWidth = this.boxWidth;
34606 if(this.containerWidth < boxWidth){
34607 boxWidth = this.containerWidth;
34610 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34612 this.el.setHeight(boxWidth);
34616 getContainerWidth : function()
34618 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34621 layoutItems : function( isInstant )
34623 Roo.log(this.bricks);
34625 var items = Roo.apply([], this.bricks);
34627 if(this.isHorizontal){
34628 this._horizontalLayoutItems( items , isInstant );
34632 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34633 // this._verticalAlternativeLayoutItems( items , isInstant );
34637 this._verticalLayoutItems( items , isInstant );
34641 _verticalLayoutItems : function ( items , isInstant)
34643 if ( !items || !items.length ) {
34648 ['xs', 'xs', 'xs', 'tall'],
34649 ['xs', 'xs', 'tall'],
34650 ['xs', 'xs', 'sm'],
34651 ['xs', 'xs', 'xs'],
34657 ['sm', 'xs', 'xs'],
34661 ['tall', 'xs', 'xs', 'xs'],
34662 ['tall', 'xs', 'xs'],
34674 Roo.each(items, function(item, k){
34676 switch (item.size) {
34677 // these layouts take up a full box,
34688 boxes.push([item]);
34711 var filterPattern = function(box, length)
34719 var pattern = box.slice(0, length);
34723 Roo.each(pattern, function(i){
34724 format.push(i.size);
34727 Roo.each(standard, function(s){
34729 if(String(s) != String(format)){
34738 if(!match && length == 1){
34743 filterPattern(box, length - 1);
34747 queue.push(pattern);
34749 box = box.slice(length, box.length);
34751 filterPattern(box, 4);
34757 Roo.each(boxes, function(box, k){
34763 if(box.length == 1){
34768 filterPattern(box, 4);
34772 this._processVerticalLayoutQueue( queue, isInstant );
34776 // _verticalAlternativeLayoutItems : function( items , isInstant )
34778 // if ( !items || !items.length ) {
34782 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34786 _horizontalLayoutItems : function ( items , isInstant)
34788 if ( !items || !items.length || items.length < 3) {
34794 var eItems = items.slice(0, 3);
34796 items = items.slice(3, items.length);
34799 ['xs', 'xs', 'xs', 'wide'],
34800 ['xs', 'xs', 'wide'],
34801 ['xs', 'xs', 'sm'],
34802 ['xs', 'xs', 'xs'],
34808 ['sm', 'xs', 'xs'],
34812 ['wide', 'xs', 'xs', 'xs'],
34813 ['wide', 'xs', 'xs'],
34826 Roo.each(items, function(item, k){
34828 switch (item.size) {
34839 boxes.push([item]);
34863 var filterPattern = function(box, length)
34871 var pattern = box.slice(0, length);
34875 Roo.each(pattern, function(i){
34876 format.push(i.size);
34879 Roo.each(standard, function(s){
34881 if(String(s) != String(format)){
34890 if(!match && length == 1){
34895 filterPattern(box, length - 1);
34899 queue.push(pattern);
34901 box = box.slice(length, box.length);
34903 filterPattern(box, 4);
34909 Roo.each(boxes, function(box, k){
34915 if(box.length == 1){
34920 filterPattern(box, 4);
34927 var pos = this.el.getBox(true);
34931 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34933 var hit_end = false;
34935 Roo.each(queue, function(box){
34939 Roo.each(box, function(b){
34941 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34951 Roo.each(box, function(b){
34953 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34956 mx = Math.max(mx, b.x);
34960 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34964 Roo.each(box, function(b){
34966 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34980 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34983 /** Sets position of item in DOM
34984 * @param {Element} item
34985 * @param {Number} x - horizontal position
34986 * @param {Number} y - vertical position
34987 * @param {Boolean} isInstant - disables transitions
34989 _processVerticalLayoutQueue : function( queue, isInstant )
34991 var pos = this.el.getBox(true);
34996 for (var i = 0; i < this.cols; i++){
35000 Roo.each(queue, function(box, k){
35002 var col = k % this.cols;
35004 Roo.each(box, function(b,kk){
35006 b.el.position('absolute');
35008 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35009 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35011 if(b.size == 'md-left' || b.size == 'md-right'){
35012 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35013 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35016 b.el.setWidth(width);
35017 b.el.setHeight(height);
35019 b.el.select('iframe',true).setSize(width,height);
35023 for (var i = 0; i < this.cols; i++){
35025 if(maxY[i] < maxY[col]){
35030 col = Math.min(col, i);
35034 x = pos.x + col * (this.colWidth + this.padWidth);
35038 var positions = [];
35040 switch (box.length){
35042 positions = this.getVerticalOneBoxColPositions(x, y, box);
35045 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35048 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35051 positions = this.getVerticalFourBoxColPositions(x, y, box);
35057 Roo.each(box, function(b,kk){
35059 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35061 var sz = b.el.getSize();
35063 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35071 for (var i = 0; i < this.cols; i++){
35072 mY = Math.max(mY, maxY[i]);
35075 this.el.setHeight(mY - pos.y);
35079 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35081 // var pos = this.el.getBox(true);
35084 // var maxX = pos.right;
35086 // var maxHeight = 0;
35088 // Roo.each(items, function(item, k){
35092 // item.el.position('absolute');
35094 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35096 // item.el.setWidth(width);
35098 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35100 // item.el.setHeight(height);
35103 // item.el.setXY([x, y], isInstant ? false : true);
35105 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35108 // y = y + height + this.alternativePadWidth;
35110 // maxHeight = maxHeight + height + this.alternativePadWidth;
35114 // this.el.setHeight(maxHeight);
35118 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35120 var pos = this.el.getBox(true);
35125 var maxX = pos.right;
35127 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35129 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35131 Roo.each(queue, function(box, k){
35133 Roo.each(box, function(b, kk){
35135 b.el.position('absolute');
35137 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35138 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35140 if(b.size == 'md-left' || b.size == 'md-right'){
35141 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35142 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35145 b.el.setWidth(width);
35146 b.el.setHeight(height);
35154 var positions = [];
35156 switch (box.length){
35158 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35161 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35164 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35167 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35173 Roo.each(box, function(b,kk){
35175 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35177 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35185 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35187 Roo.each(eItems, function(b,k){
35189 b.size = (k == 0) ? 'sm' : 'xs';
35190 b.x = (k == 0) ? 2 : 1;
35191 b.y = (k == 0) ? 2 : 1;
35193 b.el.position('absolute');
35195 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35197 b.el.setWidth(width);
35199 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35201 b.el.setHeight(height);
35205 var positions = [];
35208 x : maxX - this.unitWidth * 2 - this.gutter,
35213 x : maxX - this.unitWidth,
35214 y : minY + (this.unitWidth + this.gutter) * 2
35218 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35222 Roo.each(eItems, function(b,k){
35224 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35230 getVerticalOneBoxColPositions : function(x, y, box)
35234 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35236 if(box[0].size == 'md-left'){
35240 if(box[0].size == 'md-right'){
35245 x : x + (this.unitWidth + this.gutter) * rand,
35252 getVerticalTwoBoxColPositions : function(x, y, box)
35256 if(box[0].size == 'xs'){
35260 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35264 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35278 x : x + (this.unitWidth + this.gutter) * 2,
35279 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35286 getVerticalThreeBoxColPositions : function(x, y, box)
35290 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35298 x : x + (this.unitWidth + this.gutter) * 1,
35303 x : x + (this.unitWidth + this.gutter) * 2,
35311 if(box[0].size == 'xs' && box[1].size == 'xs'){
35320 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35324 x : x + (this.unitWidth + this.gutter) * 1,
35338 x : x + (this.unitWidth + this.gutter) * 2,
35343 x : x + (this.unitWidth + this.gutter) * 2,
35344 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35351 getVerticalFourBoxColPositions : function(x, y, box)
35355 if(box[0].size == 'xs'){
35364 y : y + (this.unitHeight + this.gutter) * 1
35369 y : y + (this.unitHeight + this.gutter) * 2
35373 x : x + (this.unitWidth + this.gutter) * 1,
35387 x : x + (this.unitWidth + this.gutter) * 2,
35392 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35393 y : y + (this.unitHeight + this.gutter) * 1
35397 x : x + (this.unitWidth + this.gutter) * 2,
35398 y : y + (this.unitWidth + this.gutter) * 2
35405 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35409 if(box[0].size == 'md-left'){
35411 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35418 if(box[0].size == 'md-right'){
35420 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35421 y : minY + (this.unitWidth + this.gutter) * 1
35427 var rand = Math.floor(Math.random() * (4 - box[0].y));
35430 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35431 y : minY + (this.unitWidth + this.gutter) * rand
35438 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35442 if(box[0].size == 'xs'){
35445 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35450 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35451 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35459 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35464 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35465 y : minY + (this.unitWidth + this.gutter) * 2
35472 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35476 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35479 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35484 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35485 y : minY + (this.unitWidth + this.gutter) * 1
35489 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35490 y : minY + (this.unitWidth + this.gutter) * 2
35497 if(box[0].size == 'xs' && box[1].size == 'xs'){
35500 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35505 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35510 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35511 y : minY + (this.unitWidth + this.gutter) * 1
35519 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35524 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35525 y : minY + (this.unitWidth + this.gutter) * 2
35529 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35530 y : minY + (this.unitWidth + this.gutter) * 2
35537 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35541 if(box[0].size == 'xs'){
35544 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35549 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35554 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),
35559 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35560 y : minY + (this.unitWidth + this.gutter) * 1
35568 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35573 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35574 y : minY + (this.unitWidth + this.gutter) * 2
35578 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35579 y : minY + (this.unitWidth + this.gutter) * 2
35583 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),
35584 y : minY + (this.unitWidth + this.gutter) * 2
35592 * remove a Masonry Brick
35593 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35595 removeBrick : function(brick_id)
35601 for (var i = 0; i<this.bricks.length; i++) {
35602 if (this.bricks[i].id == brick_id) {
35603 this.bricks.splice(i,1);
35604 this.el.dom.removeChild(Roo.get(brick_id).dom);
35611 * adds a Masonry Brick
35612 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35614 addBrick : function(cfg)
35616 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35617 //this.register(cn);
35618 cn.parentId = this.id;
35619 cn.render(this.el);
35624 * register a Masonry Brick
35625 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35628 register : function(brick)
35630 this.bricks.push(brick);
35631 brick.masonryId = this.id;
35635 * clear all the Masonry Brick
35637 clearAll : function()
35640 //this.getChildContainer().dom.innerHTML = "";
35641 this.el.dom.innerHTML = '';
35644 getSelected : function()
35646 if (!this.selectedBrick) {
35650 return this.selectedBrick;
35654 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35658 * register a Masonry Layout
35659 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35662 register : function(layout)
35664 this.groups[layout.id] = layout;
35667 * fetch a Masonry Layout based on the masonry layout ID
35668 * @param {string} the masonry layout to add
35669 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35672 get: function(layout_id) {
35673 if (typeof(this.groups[layout_id]) == 'undefined') {
35676 return this.groups[layout_id] ;
35688 * http://masonry.desandro.com
35690 * The idea is to render all the bricks based on vertical width...
35692 * The original code extends 'outlayer' - we might need to use that....
35698 * @class Roo.bootstrap.LayoutMasonryAuto
35699 * @extends Roo.bootstrap.Component
35700 * Bootstrap Layout Masonry class
35703 * Create a new Element
35704 * @param {Object} config The config object
35707 Roo.bootstrap.LayoutMasonryAuto = function(config){
35708 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35711 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35714 * @cfg {Boolean} isFitWidth - resize the width..
35716 isFitWidth : false, // options..
35718 * @cfg {Boolean} isOriginLeft = left align?
35720 isOriginLeft : true,
35722 * @cfg {Boolean} isOriginTop = top align?
35724 isOriginTop : false,
35726 * @cfg {Boolean} isLayoutInstant = no animation?
35728 isLayoutInstant : false, // needed?
35730 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35732 isResizingContainer : true,
35734 * @cfg {Number} columnWidth width of the columns
35740 * @cfg {Number} maxCols maximum number of columns
35745 * @cfg {Number} padHeight padding below box..
35751 * @cfg {Boolean} isAutoInitial defalut true
35754 isAutoInitial : true,
35760 initialColumnWidth : 0,
35761 currentSize : null,
35763 colYs : null, // array.
35770 bricks: null, //CompositeElement
35771 cols : 0, // array?
35772 // element : null, // wrapped now this.el
35773 _isLayoutInited : null,
35776 getAutoCreate : function(){
35780 cls: 'blog-masonary-wrapper ' + this.cls,
35782 cls : 'mas-boxes masonary'
35789 getChildContainer: function( )
35791 if (this.boxesEl) {
35792 return this.boxesEl;
35795 this.boxesEl = this.el.select('.mas-boxes').first();
35797 return this.boxesEl;
35801 initEvents : function()
35805 if(this.isAutoInitial){
35806 Roo.log('hook children rendered');
35807 this.on('childrenrendered', function() {
35808 Roo.log('children rendered');
35815 initial : function()
35817 this.reloadItems();
35819 this.currentSize = this.el.getBox(true);
35821 /// was window resize... - let's see if this works..
35822 Roo.EventManager.onWindowResize(this.resize, this);
35824 if(!this.isAutoInitial){
35829 this.layout.defer(500,this);
35832 reloadItems: function()
35834 this.bricks = this.el.select('.masonry-brick', true);
35836 this.bricks.each(function(b) {
35837 //Roo.log(b.getSize());
35838 if (!b.attr('originalwidth')) {
35839 b.attr('originalwidth', b.getSize().width);
35844 Roo.log(this.bricks.elements.length);
35847 resize : function()
35850 var cs = this.el.getBox(true);
35852 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35853 Roo.log("no change in with or X");
35856 this.currentSize = cs;
35860 layout : function()
35863 this._resetLayout();
35864 //this._manageStamps();
35866 // don't animate first layout
35867 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35868 this.layoutItems( isInstant );
35870 // flag for initalized
35871 this._isLayoutInited = true;
35874 layoutItems : function( isInstant )
35876 //var items = this._getItemsForLayout( this.items );
35877 // original code supports filtering layout items.. we just ignore it..
35879 this._layoutItems( this.bricks , isInstant );
35881 this._postLayout();
35883 _layoutItems : function ( items , isInstant)
35885 //this.fireEvent( 'layout', this, items );
35888 if ( !items || !items.elements.length ) {
35889 // no items, emit event with empty array
35894 items.each(function(item) {
35895 Roo.log("layout item");
35897 // get x/y object from method
35898 var position = this._getItemLayoutPosition( item );
35900 position.item = item;
35901 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35902 queue.push( position );
35905 this._processLayoutQueue( queue );
35907 /** Sets position of item in DOM
35908 * @param {Element} item
35909 * @param {Number} x - horizontal position
35910 * @param {Number} y - vertical position
35911 * @param {Boolean} isInstant - disables transitions
35913 _processLayoutQueue : function( queue )
35915 for ( var i=0, len = queue.length; i < len; i++ ) {
35916 var obj = queue[i];
35917 obj.item.position('absolute');
35918 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35924 * Any logic you want to do after each layout,
35925 * i.e. size the container
35927 _postLayout : function()
35929 this.resizeContainer();
35932 resizeContainer : function()
35934 if ( !this.isResizingContainer ) {
35937 var size = this._getContainerSize();
35939 this.el.setSize(size.width,size.height);
35940 this.boxesEl.setSize(size.width,size.height);
35946 _resetLayout : function()
35948 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35949 this.colWidth = this.el.getWidth();
35950 //this.gutter = this.el.getWidth();
35952 this.measureColumns();
35958 this.colYs.push( 0 );
35964 measureColumns : function()
35966 this.getContainerWidth();
35967 // if columnWidth is 0, default to outerWidth of first item
35968 if ( !this.columnWidth ) {
35969 var firstItem = this.bricks.first();
35970 Roo.log(firstItem);
35971 this.columnWidth = this.containerWidth;
35972 if (firstItem && firstItem.attr('originalwidth') ) {
35973 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35975 // columnWidth fall back to item of first element
35976 Roo.log("set column width?");
35977 this.initialColumnWidth = this.columnWidth ;
35979 // if first elem has no width, default to size of container
35984 if (this.initialColumnWidth) {
35985 this.columnWidth = this.initialColumnWidth;
35990 // column width is fixed at the top - however if container width get's smaller we should
35993 // this bit calcs how man columns..
35995 var columnWidth = this.columnWidth += this.gutter;
35997 // calculate columns
35998 var containerWidth = this.containerWidth + this.gutter;
36000 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36001 // fix rounding errors, typically with gutters
36002 var excess = columnWidth - containerWidth % columnWidth;
36005 // if overshoot is less than a pixel, round up, otherwise floor it
36006 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36007 cols = Math[ mathMethod ]( cols );
36008 this.cols = Math.max( cols, 1 );
36009 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36011 // padding positioning..
36012 var totalColWidth = this.cols * this.columnWidth;
36013 var padavail = this.containerWidth - totalColWidth;
36014 // so for 2 columns - we need 3 'pads'
36016 var padNeeded = (1+this.cols) * this.padWidth;
36018 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36020 this.columnWidth += padExtra
36021 //this.padWidth = Math.floor(padavail / ( this.cols));
36023 // adjust colum width so that padding is fixed??
36025 // we have 3 columns ... total = width * 3
36026 // we have X left over... that should be used by
36028 //if (this.expandC) {
36036 getContainerWidth : function()
36038 /* // container is parent if fit width
36039 var container = this.isFitWidth ? this.element.parentNode : this.element;
36040 // check that this.size and size are there
36041 // IE8 triggers resize on body size change, so they might not be
36043 var size = getSize( container ); //FIXME
36044 this.containerWidth = size && size.innerWidth; //FIXME
36047 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36051 _getItemLayoutPosition : function( item ) // what is item?
36053 // we resize the item to our columnWidth..
36055 item.setWidth(this.columnWidth);
36056 item.autoBoxAdjust = false;
36058 var sz = item.getSize();
36060 // how many columns does this brick span
36061 var remainder = this.containerWidth % this.columnWidth;
36063 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36064 // round if off by 1 pixel, otherwise use ceil
36065 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36066 colSpan = Math.min( colSpan, this.cols );
36068 // normally this should be '1' as we dont' currently allow multi width columns..
36070 var colGroup = this._getColGroup( colSpan );
36071 // get the minimum Y value from the columns
36072 var minimumY = Math.min.apply( Math, colGroup );
36073 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36075 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36077 // position the brick
36079 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36080 y: this.currentSize.y + minimumY + this.padHeight
36084 // apply setHeight to necessary columns
36085 var setHeight = minimumY + sz.height + this.padHeight;
36086 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36088 var setSpan = this.cols + 1 - colGroup.length;
36089 for ( var i = 0; i < setSpan; i++ ) {
36090 this.colYs[ shortColIndex + i ] = setHeight ;
36097 * @param {Number} colSpan - number of columns the element spans
36098 * @returns {Array} colGroup
36100 _getColGroup : function( colSpan )
36102 if ( colSpan < 2 ) {
36103 // if brick spans only one column, use all the column Ys
36108 // how many different places could this brick fit horizontally
36109 var groupCount = this.cols + 1 - colSpan;
36110 // for each group potential horizontal position
36111 for ( var i = 0; i < groupCount; i++ ) {
36112 // make an array of colY values for that one group
36113 var groupColYs = this.colYs.slice( i, i + colSpan );
36114 // and get the max value of the array
36115 colGroup[i] = Math.max.apply( Math, groupColYs );
36120 _manageStamp : function( stamp )
36122 var stampSize = stamp.getSize();
36123 var offset = stamp.getBox();
36124 // get the columns that this stamp affects
36125 var firstX = this.isOriginLeft ? offset.x : offset.right;
36126 var lastX = firstX + stampSize.width;
36127 var firstCol = Math.floor( firstX / this.columnWidth );
36128 firstCol = Math.max( 0, firstCol );
36130 var lastCol = Math.floor( lastX / this.columnWidth );
36131 // lastCol should not go over if multiple of columnWidth #425
36132 lastCol -= lastX % this.columnWidth ? 0 : 1;
36133 lastCol = Math.min( this.cols - 1, lastCol );
36135 // set colYs to bottom of the stamp
36136 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36139 for ( var i = firstCol; i <= lastCol; i++ ) {
36140 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36145 _getContainerSize : function()
36147 this.maxY = Math.max.apply( Math, this.colYs );
36152 if ( this.isFitWidth ) {
36153 size.width = this._getContainerFitWidth();
36159 _getContainerFitWidth : function()
36161 var unusedCols = 0;
36162 // count unused columns
36165 if ( this.colYs[i] !== 0 ) {
36170 // fit container to columns that have been used
36171 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36174 needsResizeLayout : function()
36176 var previousWidth = this.containerWidth;
36177 this.getContainerWidth();
36178 return previousWidth !== this.containerWidth;
36193 * @class Roo.bootstrap.MasonryBrick
36194 * @extends Roo.bootstrap.Component
36195 * Bootstrap MasonryBrick class
36198 * Create a new MasonryBrick
36199 * @param {Object} config The config object
36202 Roo.bootstrap.MasonryBrick = function(config){
36204 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36206 Roo.bootstrap.MasonryBrick.register(this);
36212 * When a MasonryBrick is clcik
36213 * @param {Roo.bootstrap.MasonryBrick} this
36214 * @param {Roo.EventObject} e
36220 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36223 * @cfg {String} title
36227 * @cfg {String} html
36231 * @cfg {String} bgimage
36235 * @cfg {String} videourl
36239 * @cfg {String} cls
36243 * @cfg {String} href
36247 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36252 * @cfg {String} placetitle (center|bottom)
36257 * @cfg {Boolean} isFitContainer defalut true
36259 isFitContainer : true,
36262 * @cfg {Boolean} preventDefault defalut false
36264 preventDefault : false,
36267 * @cfg {Boolean} inverse defalut false
36269 maskInverse : false,
36271 getAutoCreate : function()
36273 if(!this.isFitContainer){
36274 return this.getSplitAutoCreate();
36277 var cls = 'masonry-brick masonry-brick-full';
36279 if(this.href.length){
36280 cls += ' masonry-brick-link';
36283 if(this.bgimage.length){
36284 cls += ' masonry-brick-image';
36287 if(this.maskInverse){
36288 cls += ' mask-inverse';
36291 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36292 cls += ' enable-mask';
36296 cls += ' masonry-' + this.size + '-brick';
36299 if(this.placetitle.length){
36301 switch (this.placetitle) {
36303 cls += ' masonry-center-title';
36306 cls += ' masonry-bottom-title';
36313 if(!this.html.length && !this.bgimage.length){
36314 cls += ' masonry-center-title';
36317 if(!this.html.length && this.bgimage.length){
36318 cls += ' masonry-bottom-title';
36323 cls += ' ' + this.cls;
36327 tag: (this.href.length) ? 'a' : 'div',
36332 cls: 'masonry-brick-mask'
36336 cls: 'masonry-brick-paragraph',
36342 if(this.href.length){
36343 cfg.href = this.href;
36346 var cn = cfg.cn[1].cn;
36348 if(this.title.length){
36351 cls: 'masonry-brick-title',
36356 if(this.html.length){
36359 cls: 'masonry-brick-text',
36364 if (!this.title.length && !this.html.length) {
36365 cfg.cn[1].cls += ' hide';
36368 if(this.bgimage.length){
36371 cls: 'masonry-brick-image-view',
36376 if(this.videourl.length){
36377 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36378 // youtube support only?
36381 cls: 'masonry-brick-image-view',
36384 allowfullscreen : true
36392 getSplitAutoCreate : function()
36394 var cls = 'masonry-brick masonry-brick-split';
36396 if(this.href.length){
36397 cls += ' masonry-brick-link';
36400 if(this.bgimage.length){
36401 cls += ' masonry-brick-image';
36405 cls += ' masonry-' + this.size + '-brick';
36408 switch (this.placetitle) {
36410 cls += ' masonry-center-title';
36413 cls += ' masonry-bottom-title';
36416 if(!this.bgimage.length){
36417 cls += ' masonry-center-title';
36420 if(this.bgimage.length){
36421 cls += ' masonry-bottom-title';
36427 cls += ' ' + this.cls;
36431 tag: (this.href.length) ? 'a' : 'div',
36436 cls: 'masonry-brick-split-head',
36440 cls: 'masonry-brick-paragraph',
36447 cls: 'masonry-brick-split-body',
36453 if(this.href.length){
36454 cfg.href = this.href;
36457 if(this.title.length){
36458 cfg.cn[0].cn[0].cn.push({
36460 cls: 'masonry-brick-title',
36465 if(this.html.length){
36466 cfg.cn[1].cn.push({
36468 cls: 'masonry-brick-text',
36473 if(this.bgimage.length){
36474 cfg.cn[0].cn.push({
36476 cls: 'masonry-brick-image-view',
36481 if(this.videourl.length){
36482 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36483 // youtube support only?
36484 cfg.cn[0].cn.cn.push({
36486 cls: 'masonry-brick-image-view',
36489 allowfullscreen : true
36496 initEvents: function()
36498 switch (this.size) {
36531 this.el.on('touchstart', this.onTouchStart, this);
36532 this.el.on('touchmove', this.onTouchMove, this);
36533 this.el.on('touchend', this.onTouchEnd, this);
36534 this.el.on('contextmenu', this.onContextMenu, this);
36536 this.el.on('mouseenter' ,this.enter, this);
36537 this.el.on('mouseleave', this.leave, this);
36538 this.el.on('click', this.onClick, this);
36541 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36542 this.parent().bricks.push(this);
36547 onClick: function(e, el)
36549 var time = this.endTimer - this.startTimer;
36550 // Roo.log(e.preventDefault());
36553 e.preventDefault();
36558 if(!this.preventDefault){
36562 e.preventDefault();
36564 if (this.activeClass != '') {
36565 this.selectBrick();
36568 this.fireEvent('click', this, e);
36571 enter: function(e, el)
36573 e.preventDefault();
36575 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36579 if(this.bgimage.length && this.html.length){
36580 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36584 leave: function(e, el)
36586 e.preventDefault();
36588 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36592 if(this.bgimage.length && this.html.length){
36593 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36597 onTouchStart: function(e, el)
36599 // e.preventDefault();
36601 this.touchmoved = false;
36603 if(!this.isFitContainer){
36607 if(!this.bgimage.length || !this.html.length){
36611 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36613 this.timer = new Date().getTime();
36617 onTouchMove: function(e, el)
36619 this.touchmoved = true;
36622 onContextMenu : function(e,el)
36624 e.preventDefault();
36625 e.stopPropagation();
36629 onTouchEnd: function(e, el)
36631 // e.preventDefault();
36633 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36640 if(!this.bgimage.length || !this.html.length){
36642 if(this.href.length){
36643 window.location.href = this.href;
36649 if(!this.isFitContainer){
36653 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36655 window.location.href = this.href;
36658 //selection on single brick only
36659 selectBrick : function() {
36661 if (!this.parentId) {
36665 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36666 var index = m.selectedBrick.indexOf(this.id);
36669 m.selectedBrick.splice(index,1);
36670 this.el.removeClass(this.activeClass);
36674 for(var i = 0; i < m.selectedBrick.length; i++) {
36675 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36676 b.el.removeClass(b.activeClass);
36679 m.selectedBrick = [];
36681 m.selectedBrick.push(this.id);
36682 this.el.addClass(this.activeClass);
36686 isSelected : function(){
36687 return this.el.hasClass(this.activeClass);
36692 Roo.apply(Roo.bootstrap.MasonryBrick, {
36695 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36697 * register a Masonry Brick
36698 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36701 register : function(brick)
36703 //this.groups[brick.id] = brick;
36704 this.groups.add(brick.id, brick);
36707 * fetch a masonry brick based on the masonry brick ID
36708 * @param {string} the masonry brick to add
36709 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36712 get: function(brick_id)
36714 // if (typeof(this.groups[brick_id]) == 'undefined') {
36717 // return this.groups[brick_id] ;
36719 if(this.groups.key(brick_id)) {
36720 return this.groups.key(brick_id);
36738 * @class Roo.bootstrap.Brick
36739 * @extends Roo.bootstrap.Component
36740 * Bootstrap Brick class
36743 * Create a new Brick
36744 * @param {Object} config The config object
36747 Roo.bootstrap.Brick = function(config){
36748 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36754 * When a Brick is click
36755 * @param {Roo.bootstrap.Brick} this
36756 * @param {Roo.EventObject} e
36762 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36765 * @cfg {String} title
36769 * @cfg {String} html
36773 * @cfg {String} bgimage
36777 * @cfg {String} cls
36781 * @cfg {String} href
36785 * @cfg {String} video
36789 * @cfg {Boolean} square
36793 getAutoCreate : function()
36795 var cls = 'roo-brick';
36797 if(this.href.length){
36798 cls += ' roo-brick-link';
36801 if(this.bgimage.length){
36802 cls += ' roo-brick-image';
36805 if(!this.html.length && !this.bgimage.length){
36806 cls += ' roo-brick-center-title';
36809 if(!this.html.length && this.bgimage.length){
36810 cls += ' roo-brick-bottom-title';
36814 cls += ' ' + this.cls;
36818 tag: (this.href.length) ? 'a' : 'div',
36823 cls: 'roo-brick-paragraph',
36829 if(this.href.length){
36830 cfg.href = this.href;
36833 var cn = cfg.cn[0].cn;
36835 if(this.title.length){
36838 cls: 'roo-brick-title',
36843 if(this.html.length){
36846 cls: 'roo-brick-text',
36853 if(this.bgimage.length){
36856 cls: 'roo-brick-image-view',
36864 initEvents: function()
36866 if(this.title.length || this.html.length){
36867 this.el.on('mouseenter' ,this.enter, this);
36868 this.el.on('mouseleave', this.leave, this);
36871 Roo.EventManager.onWindowResize(this.resize, this);
36873 if(this.bgimage.length){
36874 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36875 this.imageEl.on('load', this.onImageLoad, this);
36882 onImageLoad : function()
36887 resize : function()
36889 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36891 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36893 if(this.bgimage.length){
36894 var image = this.el.select('.roo-brick-image-view', true).first();
36896 image.setWidth(paragraph.getWidth());
36899 image.setHeight(paragraph.getWidth());
36902 this.el.setHeight(image.getHeight());
36903 paragraph.setHeight(image.getHeight());
36909 enter: function(e, el)
36911 e.preventDefault();
36913 if(this.bgimage.length){
36914 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36915 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36919 leave: function(e, el)
36921 e.preventDefault();
36923 if(this.bgimage.length){
36924 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36925 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36940 * @class Roo.bootstrap.NumberField
36941 * @extends Roo.bootstrap.Input
36942 * Bootstrap NumberField class
36948 * Create a new NumberField
36949 * @param {Object} config The config object
36952 Roo.bootstrap.NumberField = function(config){
36953 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36956 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36959 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36961 allowDecimals : true,
36963 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36965 decimalSeparator : ".",
36967 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36969 decimalPrecision : 2,
36971 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36973 allowNegative : true,
36976 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36980 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36982 minValue : Number.NEGATIVE_INFINITY,
36984 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36986 maxValue : Number.MAX_VALUE,
36988 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36990 minText : "The minimum value for this field is {0}",
36992 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36994 maxText : "The maximum value for this field is {0}",
36996 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36997 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36999 nanText : "{0} is not a valid number",
37001 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37003 thousandsDelimiter : false,
37005 * @cfg {String} valueAlign alignment of value
37007 valueAlign : "left",
37009 getAutoCreate : function()
37011 var hiddenInput = {
37015 cls: 'hidden-number-input'
37019 hiddenInput.name = this.name;
37024 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37026 this.name = hiddenInput.name;
37028 if(cfg.cn.length > 0) {
37029 cfg.cn.push(hiddenInput);
37036 initEvents : function()
37038 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37040 var allowed = "0123456789";
37042 if(this.allowDecimals){
37043 allowed += this.decimalSeparator;
37046 if(this.allowNegative){
37050 if(this.thousandsDelimiter) {
37054 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37056 var keyPress = function(e){
37058 var k = e.getKey();
37060 var c = e.getCharCode();
37063 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37064 allowed.indexOf(String.fromCharCode(c)) === -1
37070 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37074 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37079 this.el.on("keypress", keyPress, this);
37082 validateValue : function(value)
37085 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37089 var num = this.parseValue(value);
37092 this.markInvalid(String.format(this.nanText, value));
37096 if(num < this.minValue){
37097 this.markInvalid(String.format(this.minText, this.minValue));
37101 if(num > this.maxValue){
37102 this.markInvalid(String.format(this.maxText, this.maxValue));
37109 getValue : function()
37111 var v = this.hiddenEl().getValue();
37113 return this.fixPrecision(this.parseValue(v));
37116 parseValue : function(value)
37118 if(this.thousandsDelimiter) {
37120 r = new RegExp(",", "g");
37121 value = value.replace(r, "");
37124 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37125 return isNaN(value) ? '' : value;
37128 fixPrecision : function(value)
37130 if(this.thousandsDelimiter) {
37132 r = new RegExp(",", "g");
37133 value = value.replace(r, "");
37136 var nan = isNaN(value);
37138 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37139 return nan ? '' : value;
37141 return parseFloat(value).toFixed(this.decimalPrecision);
37144 setValue : function(v)
37146 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37152 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37154 this.inputEl().dom.value = (v == '') ? '' :
37155 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37157 if(!this.allowZero && v === '0') {
37158 this.hiddenEl().dom.value = '';
37159 this.inputEl().dom.value = '';
37166 decimalPrecisionFcn : function(v)
37168 return Math.floor(v);
37171 beforeBlur : function()
37173 var v = this.parseValue(this.getRawValue());
37175 if(v || v === 0 || v === ''){
37180 hiddenEl : function()
37182 return this.el.select('input.hidden-number-input',true).first();
37194 * @class Roo.bootstrap.DocumentSlider
37195 * @extends Roo.bootstrap.Component
37196 * Bootstrap DocumentSlider class
37199 * Create a new DocumentViewer
37200 * @param {Object} config The config object
37203 Roo.bootstrap.DocumentSlider = function(config){
37204 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37211 * Fire after initEvent
37212 * @param {Roo.bootstrap.DocumentSlider} this
37217 * Fire after update
37218 * @param {Roo.bootstrap.DocumentSlider} this
37224 * @param {Roo.bootstrap.DocumentSlider} this
37230 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37236 getAutoCreate : function()
37240 cls : 'roo-document-slider',
37244 cls : 'roo-document-slider-header',
37248 cls : 'roo-document-slider-header-title'
37254 cls : 'roo-document-slider-body',
37258 cls : 'roo-document-slider-prev',
37262 cls : 'fa fa-chevron-left'
37268 cls : 'roo-document-slider-thumb',
37272 cls : 'roo-document-slider-image'
37278 cls : 'roo-document-slider-next',
37282 cls : 'fa fa-chevron-right'
37294 initEvents : function()
37296 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37297 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37299 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37300 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37302 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37303 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37305 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37306 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37308 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37309 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37311 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37312 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37314 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37315 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37317 this.thumbEl.on('click', this.onClick, this);
37319 this.prevIndicator.on('click', this.prev, this);
37321 this.nextIndicator.on('click', this.next, this);
37325 initial : function()
37327 if(this.files.length){
37328 this.indicator = 1;
37332 this.fireEvent('initial', this);
37335 update : function()
37337 this.imageEl.attr('src', this.files[this.indicator - 1]);
37339 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37341 this.prevIndicator.show();
37343 if(this.indicator == 1){
37344 this.prevIndicator.hide();
37347 this.nextIndicator.show();
37349 if(this.indicator == this.files.length){
37350 this.nextIndicator.hide();
37353 this.thumbEl.scrollTo('top');
37355 this.fireEvent('update', this);
37358 onClick : function(e)
37360 e.preventDefault();
37362 this.fireEvent('click', this);
37367 e.preventDefault();
37369 this.indicator = Math.max(1, this.indicator - 1);
37376 e.preventDefault();
37378 this.indicator = Math.min(this.files.length, this.indicator + 1);
37392 * @class Roo.bootstrap.RadioSet
37393 * @extends Roo.bootstrap.Input
37394 * Bootstrap RadioSet class
37395 * @cfg {String} indicatorpos (left|right) default left
37396 * @cfg {Boolean} inline (true|false) inline the element (default true)
37397 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37399 * Create a new RadioSet
37400 * @param {Object} config The config object
37403 Roo.bootstrap.RadioSet = function(config){
37405 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37409 Roo.bootstrap.RadioSet.register(this);
37414 * Fires when the element is checked or unchecked.
37415 * @param {Roo.bootstrap.RadioSet} this This radio
37416 * @param {Roo.bootstrap.Radio} item The checked item
37421 * Fires when the element is click.
37422 * @param {Roo.bootstrap.RadioSet} this This radio set
37423 * @param {Roo.bootstrap.Radio} item The checked item
37424 * @param {Roo.EventObject} e The event object
37431 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37439 indicatorpos : 'left',
37441 getAutoCreate : function()
37445 cls : 'roo-radio-set-label',
37449 html : this.fieldLabel
37453 if (Roo.bootstrap.version == 3) {
37456 if(this.indicatorpos == 'left'){
37459 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37460 tooltip : 'This field is required'
37465 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37466 tooltip : 'This field is required'
37472 cls : 'roo-radio-set-items'
37475 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37477 if (align === 'left' && this.fieldLabel.length) {
37480 cls : "roo-radio-set-right",
37486 if(this.labelWidth > 12){
37487 label.style = "width: " + this.labelWidth + 'px';
37490 if(this.labelWidth < 13 && this.labelmd == 0){
37491 this.labelmd = this.labelWidth;
37494 if(this.labellg > 0){
37495 label.cls += ' col-lg-' + this.labellg;
37496 items.cls += ' col-lg-' + (12 - this.labellg);
37499 if(this.labelmd > 0){
37500 label.cls += ' col-md-' + this.labelmd;
37501 items.cls += ' col-md-' + (12 - this.labelmd);
37504 if(this.labelsm > 0){
37505 label.cls += ' col-sm-' + this.labelsm;
37506 items.cls += ' col-sm-' + (12 - this.labelsm);
37509 if(this.labelxs > 0){
37510 label.cls += ' col-xs-' + this.labelxs;
37511 items.cls += ' col-xs-' + (12 - this.labelxs);
37517 cls : 'roo-radio-set',
37521 cls : 'roo-radio-set-input',
37524 value : this.value ? this.value : ''
37531 if(this.weight.length){
37532 cfg.cls += ' roo-radio-' + this.weight;
37536 cfg.cls += ' roo-radio-set-inline';
37540 ['xs','sm','md','lg'].map(function(size){
37541 if (settings[size]) {
37542 cfg.cls += ' col-' + size + '-' + settings[size];
37550 initEvents : function()
37552 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37553 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37555 if(!this.fieldLabel.length){
37556 this.labelEl.hide();
37559 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37560 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37562 this.indicator = this.indicatorEl();
37564 if(this.indicator){
37565 this.indicator.addClass('invisible');
37568 this.originalValue = this.getValue();
37572 inputEl: function ()
37574 return this.el.select('.roo-radio-set-input', true).first();
37577 getChildContainer : function()
37579 return this.itemsEl;
37582 register : function(item)
37584 this.radioes.push(item);
37588 validate : function()
37590 if(this.getVisibilityEl().hasClass('hidden')){
37596 Roo.each(this.radioes, function(i){
37605 if(this.allowBlank) {
37609 if(this.disabled || valid){
37614 this.markInvalid();
37619 markValid : function()
37621 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37622 this.indicatorEl().removeClass('visible');
37623 this.indicatorEl().addClass('invisible');
37627 if (Roo.bootstrap.version == 3) {
37628 this.el.removeClass([this.invalidClass, this.validClass]);
37629 this.el.addClass(this.validClass);
37631 this.el.removeClass(['is-invalid','is-valid']);
37632 this.el.addClass(['is-valid']);
37634 this.fireEvent('valid', this);
37637 markInvalid : function(msg)
37639 if(this.allowBlank || this.disabled){
37643 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37644 this.indicatorEl().removeClass('invisible');
37645 this.indicatorEl().addClass('visible');
37647 if (Roo.bootstrap.version == 3) {
37648 this.el.removeClass([this.invalidClass, this.validClass]);
37649 this.el.addClass(this.invalidClass);
37651 this.el.removeClass(['is-invalid','is-valid']);
37652 this.el.addClass(['is-invalid']);
37655 this.fireEvent('invalid', this, msg);
37659 setValue : function(v, suppressEvent)
37661 if(this.value === v){
37668 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37671 Roo.each(this.radioes, function(i){
37673 i.el.removeClass('checked');
37676 Roo.each(this.radioes, function(i){
37678 if(i.value === v || i.value.toString() === v.toString()){
37680 i.el.addClass('checked');
37682 if(suppressEvent !== true){
37683 this.fireEvent('check', this, i);
37694 clearInvalid : function(){
37696 if(!this.el || this.preventMark){
37700 this.el.removeClass([this.invalidClass]);
37702 this.fireEvent('valid', this);
37707 Roo.apply(Roo.bootstrap.RadioSet, {
37711 register : function(set)
37713 this.groups[set.name] = set;
37716 get: function(name)
37718 if (typeof(this.groups[name]) == 'undefined') {
37722 return this.groups[name] ;
37728 * Ext JS Library 1.1.1
37729 * Copyright(c) 2006-2007, Ext JS, LLC.
37731 * Originally Released Under LGPL - original licence link has changed is not relivant.
37734 * <script type="text/javascript">
37739 * @class Roo.bootstrap.SplitBar
37740 * @extends Roo.util.Observable
37741 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37745 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37746 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37747 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37748 split.minSize = 100;
37749 split.maxSize = 600;
37750 split.animate = true;
37751 split.on('moved', splitterMoved);
37754 * Create a new SplitBar
37755 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37756 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37757 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37758 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37759 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37760 position of the SplitBar).
37762 Roo.bootstrap.SplitBar = function(cfg){
37767 // dragElement : elm
37768 // resizingElement: el,
37770 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37771 // placement : Roo.bootstrap.SplitBar.LEFT ,
37772 // existingProxy ???
37775 this.el = Roo.get(cfg.dragElement, true);
37776 this.el.dom.unselectable = "on";
37778 this.resizingEl = Roo.get(cfg.resizingElement, true);
37782 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37783 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37786 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37789 * The minimum size of the resizing element. (Defaults to 0)
37795 * The maximum size of the resizing element. (Defaults to 2000)
37798 this.maxSize = 2000;
37801 * Whether to animate the transition to the new size
37804 this.animate = false;
37807 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37810 this.useShim = false;
37815 if(!cfg.existingProxy){
37817 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37819 this.proxy = Roo.get(cfg.existingProxy).dom;
37822 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37825 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37828 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37831 this.dragSpecs = {};
37834 * @private The adapter to use to positon and resize elements
37836 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37837 this.adapter.init(this);
37839 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37841 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37842 this.el.addClass("roo-splitbar-h");
37845 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37846 this.el.addClass("roo-splitbar-v");
37852 * Fires when the splitter is moved (alias for {@link #event-moved})
37853 * @param {Roo.bootstrap.SplitBar} this
37854 * @param {Number} newSize the new width or height
37859 * Fires when the splitter is moved
37860 * @param {Roo.bootstrap.SplitBar} this
37861 * @param {Number} newSize the new width or height
37865 * @event beforeresize
37866 * Fires before the splitter is dragged
37867 * @param {Roo.bootstrap.SplitBar} this
37869 "beforeresize" : true,
37871 "beforeapply" : true
37874 Roo.util.Observable.call(this);
37877 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37878 onStartProxyDrag : function(x, y){
37879 this.fireEvent("beforeresize", this);
37881 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37883 o.enableDisplayMode("block");
37884 // all splitbars share the same overlay
37885 Roo.bootstrap.SplitBar.prototype.overlay = o;
37887 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37888 this.overlay.show();
37889 Roo.get(this.proxy).setDisplayed("block");
37890 var size = this.adapter.getElementSize(this);
37891 this.activeMinSize = this.getMinimumSize();;
37892 this.activeMaxSize = this.getMaximumSize();;
37893 var c1 = size - this.activeMinSize;
37894 var c2 = Math.max(this.activeMaxSize - size, 0);
37895 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37896 this.dd.resetConstraints();
37897 this.dd.setXConstraint(
37898 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37899 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37901 this.dd.setYConstraint(0, 0);
37903 this.dd.resetConstraints();
37904 this.dd.setXConstraint(0, 0);
37905 this.dd.setYConstraint(
37906 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37907 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37910 this.dragSpecs.startSize = size;
37911 this.dragSpecs.startPoint = [x, y];
37912 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37916 * @private Called after the drag operation by the DDProxy
37918 onEndProxyDrag : function(e){
37919 Roo.get(this.proxy).setDisplayed(false);
37920 var endPoint = Roo.lib.Event.getXY(e);
37922 this.overlay.hide();
37925 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37926 newSize = this.dragSpecs.startSize +
37927 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37928 endPoint[0] - this.dragSpecs.startPoint[0] :
37929 this.dragSpecs.startPoint[0] - endPoint[0]
37932 newSize = this.dragSpecs.startSize +
37933 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37934 endPoint[1] - this.dragSpecs.startPoint[1] :
37935 this.dragSpecs.startPoint[1] - endPoint[1]
37938 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37939 if(newSize != this.dragSpecs.startSize){
37940 if(this.fireEvent('beforeapply', this, newSize) !== false){
37941 this.adapter.setElementSize(this, newSize);
37942 this.fireEvent("moved", this, newSize);
37943 this.fireEvent("resize", this, newSize);
37949 * Get the adapter this SplitBar uses
37950 * @return The adapter object
37952 getAdapter : function(){
37953 return this.adapter;
37957 * Set the adapter this SplitBar uses
37958 * @param {Object} adapter A SplitBar adapter object
37960 setAdapter : function(adapter){
37961 this.adapter = adapter;
37962 this.adapter.init(this);
37966 * Gets the minimum size for the resizing element
37967 * @return {Number} The minimum size
37969 getMinimumSize : function(){
37970 return this.minSize;
37974 * Sets the minimum size for the resizing element
37975 * @param {Number} minSize The minimum size
37977 setMinimumSize : function(minSize){
37978 this.minSize = minSize;
37982 * Gets the maximum size for the resizing element
37983 * @return {Number} The maximum size
37985 getMaximumSize : function(){
37986 return this.maxSize;
37990 * Sets the maximum size for the resizing element
37991 * @param {Number} maxSize The maximum size
37993 setMaximumSize : function(maxSize){
37994 this.maxSize = maxSize;
37998 * Sets the initialize size for the resizing element
37999 * @param {Number} size The initial size
38001 setCurrentSize : function(size){
38002 var oldAnimate = this.animate;
38003 this.animate = false;
38004 this.adapter.setElementSize(this, size);
38005 this.animate = oldAnimate;
38009 * Destroy this splitbar.
38010 * @param {Boolean} removeEl True to remove the element
38012 destroy : function(removeEl){
38014 this.shim.remove();
38017 this.proxy.parentNode.removeChild(this.proxy);
38025 * @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.
38027 Roo.bootstrap.SplitBar.createProxy = function(dir){
38028 var proxy = new Roo.Element(document.createElement("div"));
38029 proxy.unselectable();
38030 var cls = 'roo-splitbar-proxy';
38031 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38032 document.body.appendChild(proxy.dom);
38037 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38038 * Default Adapter. It assumes the splitter and resizing element are not positioned
38039 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38041 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38044 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38045 // do nothing for now
38046 init : function(s){
38050 * Called before drag operations to get the current size of the resizing element.
38051 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38053 getElementSize : function(s){
38054 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38055 return s.resizingEl.getWidth();
38057 return s.resizingEl.getHeight();
38062 * Called after drag operations to set the size of the resizing element.
38063 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38064 * @param {Number} newSize The new size to set
38065 * @param {Function} onComplete A function to be invoked when resizing is complete
38067 setElementSize : function(s, newSize, onComplete){
38068 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38070 s.resizingEl.setWidth(newSize);
38072 onComplete(s, newSize);
38075 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38080 s.resizingEl.setHeight(newSize);
38082 onComplete(s, newSize);
38085 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38092 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38093 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38094 * Adapter that moves the splitter element to align with the resized sizing element.
38095 * Used with an absolute positioned SplitBar.
38096 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38097 * document.body, make sure you assign an id to the body element.
38099 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38100 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38101 this.container = Roo.get(container);
38104 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38105 init : function(s){
38106 this.basic.init(s);
38109 getElementSize : function(s){
38110 return this.basic.getElementSize(s);
38113 setElementSize : function(s, newSize, onComplete){
38114 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38117 moveSplitter : function(s){
38118 var yes = Roo.bootstrap.SplitBar;
38119 switch(s.placement){
38121 s.el.setX(s.resizingEl.getRight());
38124 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38127 s.el.setY(s.resizingEl.getBottom());
38130 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38137 * Orientation constant - Create a vertical SplitBar
38141 Roo.bootstrap.SplitBar.VERTICAL = 1;
38144 * Orientation constant - Create a horizontal SplitBar
38148 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38151 * Placement constant - The resizing element is to the left of the splitter element
38155 Roo.bootstrap.SplitBar.LEFT = 1;
38158 * Placement constant - The resizing element is to the right of the splitter element
38162 Roo.bootstrap.SplitBar.RIGHT = 2;
38165 * Placement constant - The resizing element is positioned above the splitter element
38169 Roo.bootstrap.SplitBar.TOP = 3;
38172 * Placement constant - The resizing element is positioned under splitter element
38176 Roo.bootstrap.SplitBar.BOTTOM = 4;
38177 Roo.namespace("Roo.bootstrap.layout");/*
38179 * Ext JS Library 1.1.1
38180 * Copyright(c) 2006-2007, Ext JS, LLC.
38182 * Originally Released Under LGPL - original licence link has changed is not relivant.
38185 * <script type="text/javascript">
38189 * @class Roo.bootstrap.layout.Manager
38190 * @extends Roo.bootstrap.Component
38191 * Base class for layout managers.
38193 Roo.bootstrap.layout.Manager = function(config)
38195 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38201 /** false to disable window resize monitoring @type Boolean */
38202 this.monitorWindowResize = true;
38207 * Fires when a layout is performed.
38208 * @param {Roo.LayoutManager} this
38212 * @event regionresized
38213 * Fires when the user resizes a region.
38214 * @param {Roo.LayoutRegion} region The resized region
38215 * @param {Number} newSize The new size (width for east/west, height for north/south)
38217 "regionresized" : true,
38219 * @event regioncollapsed
38220 * Fires when a region is collapsed.
38221 * @param {Roo.LayoutRegion} region The collapsed region
38223 "regioncollapsed" : true,
38225 * @event regionexpanded
38226 * Fires when a region is expanded.
38227 * @param {Roo.LayoutRegion} region The expanded region
38229 "regionexpanded" : true
38231 this.updating = false;
38234 this.el = Roo.get(config.el);
38240 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38245 monitorWindowResize : true,
38251 onRender : function(ct, position)
38254 this.el = Roo.get(ct);
38257 //this.fireEvent('render',this);
38261 initEvents: function()
38265 // ie scrollbar fix
38266 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38267 document.body.scroll = "no";
38268 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38269 this.el.position('relative');
38271 this.id = this.el.id;
38272 this.el.addClass("roo-layout-container");
38273 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38274 if(this.el.dom != document.body ) {
38275 this.el.on('resize', this.layout,this);
38276 this.el.on('show', this.layout,this);
38282 * Returns true if this layout is currently being updated
38283 * @return {Boolean}
38285 isUpdating : function(){
38286 return this.updating;
38290 * Suspend the LayoutManager from doing auto-layouts while
38291 * making multiple add or remove calls
38293 beginUpdate : function(){
38294 this.updating = true;
38298 * Restore auto-layouts and optionally disable the manager from performing a layout
38299 * @param {Boolean} noLayout true to disable a layout update
38301 endUpdate : function(noLayout){
38302 this.updating = false;
38308 layout: function(){
38312 onRegionResized : function(region, newSize){
38313 this.fireEvent("regionresized", region, newSize);
38317 onRegionCollapsed : function(region){
38318 this.fireEvent("regioncollapsed", region);
38321 onRegionExpanded : function(region){
38322 this.fireEvent("regionexpanded", region);
38326 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38327 * performs box-model adjustments.
38328 * @return {Object} The size as an object {width: (the width), height: (the height)}
38330 getViewSize : function()
38333 if(this.el.dom != document.body){
38334 size = this.el.getSize();
38336 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38338 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38339 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38344 * Returns the Element this layout is bound to.
38345 * @return {Roo.Element}
38347 getEl : function(){
38352 * Returns the specified region.
38353 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38354 * @return {Roo.LayoutRegion}
38356 getRegion : function(target){
38357 return this.regions[target.toLowerCase()];
38360 onWindowResize : function(){
38361 if(this.monitorWindowResize){
38368 * Ext JS Library 1.1.1
38369 * Copyright(c) 2006-2007, Ext JS, LLC.
38371 * Originally Released Under LGPL - original licence link has changed is not relivant.
38374 * <script type="text/javascript">
38377 * @class Roo.bootstrap.layout.Border
38378 * @extends Roo.bootstrap.layout.Manager
38380 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38381 * please see: examples/bootstrap/nested.html<br><br>
38383 <b>The container the layout is rendered into can be either the body element or any other element.
38384 If it is not the body element, the container needs to either be an absolute positioned element,
38385 or you will need to add "position:relative" to the css of the container. You will also need to specify
38386 the container size if it is not the body element.</b>
38389 * Create a new Border
38390 * @param {Object} config Configuration options
38392 Roo.bootstrap.layout.Border = function(config){
38393 config = config || {};
38394 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38398 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38399 if(config[region]){
38400 config[region].region = region;
38401 this.addRegion(config[region]);
38407 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38409 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38411 parent : false, // this might point to a 'nest' or a ???
38414 * Creates and adds a new region if it doesn't already exist.
38415 * @param {String} target The target region key (north, south, east, west or center).
38416 * @param {Object} config The regions config object
38417 * @return {BorderLayoutRegion} The new region
38419 addRegion : function(config)
38421 if(!this.regions[config.region]){
38422 var r = this.factory(config);
38423 this.bindRegion(r);
38425 return this.regions[config.region];
38429 bindRegion : function(r){
38430 this.regions[r.config.region] = r;
38432 r.on("visibilitychange", this.layout, this);
38433 r.on("paneladded", this.layout, this);
38434 r.on("panelremoved", this.layout, this);
38435 r.on("invalidated", this.layout, this);
38436 r.on("resized", this.onRegionResized, this);
38437 r.on("collapsed", this.onRegionCollapsed, this);
38438 r.on("expanded", this.onRegionExpanded, this);
38442 * Performs a layout update.
38444 layout : function()
38446 if(this.updating) {
38450 // render all the rebions if they have not been done alreayd?
38451 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38452 if(this.regions[region] && !this.regions[region].bodyEl){
38453 this.regions[region].onRender(this.el)
38457 var size = this.getViewSize();
38458 var w = size.width;
38459 var h = size.height;
38464 //var x = 0, y = 0;
38466 var rs = this.regions;
38467 var north = rs["north"];
38468 var south = rs["south"];
38469 var west = rs["west"];
38470 var east = rs["east"];
38471 var center = rs["center"];
38472 //if(this.hideOnLayout){ // not supported anymore
38473 //c.el.setStyle("display", "none");
38475 if(north && north.isVisible()){
38476 var b = north.getBox();
38477 var m = north.getMargins();
38478 b.width = w - (m.left+m.right);
38481 centerY = b.height + b.y + m.bottom;
38482 centerH -= centerY;
38483 north.updateBox(this.safeBox(b));
38485 if(south && south.isVisible()){
38486 var b = south.getBox();
38487 var m = south.getMargins();
38488 b.width = w - (m.left+m.right);
38490 var totalHeight = (b.height + m.top + m.bottom);
38491 b.y = h - totalHeight + m.top;
38492 centerH -= totalHeight;
38493 south.updateBox(this.safeBox(b));
38495 if(west && west.isVisible()){
38496 var b = west.getBox();
38497 var m = west.getMargins();
38498 b.height = centerH - (m.top+m.bottom);
38500 b.y = centerY + m.top;
38501 var totalWidth = (b.width + m.left + m.right);
38502 centerX += totalWidth;
38503 centerW -= totalWidth;
38504 west.updateBox(this.safeBox(b));
38506 if(east && east.isVisible()){
38507 var b = east.getBox();
38508 var m = east.getMargins();
38509 b.height = centerH - (m.top+m.bottom);
38510 var totalWidth = (b.width + m.left + m.right);
38511 b.x = w - totalWidth + m.left;
38512 b.y = centerY + m.top;
38513 centerW -= totalWidth;
38514 east.updateBox(this.safeBox(b));
38517 var m = center.getMargins();
38519 x: centerX + m.left,
38520 y: centerY + m.top,
38521 width: centerW - (m.left+m.right),
38522 height: centerH - (m.top+m.bottom)
38524 //if(this.hideOnLayout){
38525 //center.el.setStyle("display", "block");
38527 center.updateBox(this.safeBox(centerBox));
38530 this.fireEvent("layout", this);
38534 safeBox : function(box){
38535 box.width = Math.max(0, box.width);
38536 box.height = Math.max(0, box.height);
38541 * Adds a ContentPanel (or subclass) to this layout.
38542 * @param {String} target The target region key (north, south, east, west or center).
38543 * @param {Roo.ContentPanel} panel The panel to add
38544 * @return {Roo.ContentPanel} The added panel
38546 add : function(target, panel){
38548 target = target.toLowerCase();
38549 return this.regions[target].add(panel);
38553 * Remove a ContentPanel (or subclass) to this layout.
38554 * @param {String} target The target region key (north, south, east, west or center).
38555 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38556 * @return {Roo.ContentPanel} The removed panel
38558 remove : function(target, panel){
38559 target = target.toLowerCase();
38560 return this.regions[target].remove(panel);
38564 * Searches all regions for a panel with the specified id
38565 * @param {String} panelId
38566 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38568 findPanel : function(panelId){
38569 var rs = this.regions;
38570 for(var target in rs){
38571 if(typeof rs[target] != "function"){
38572 var p = rs[target].getPanel(panelId);
38582 * Searches all regions for a panel with the specified id and activates (shows) it.
38583 * @param {String/ContentPanel} panelId The panels id or the panel itself
38584 * @return {Roo.ContentPanel} The shown panel or null
38586 showPanel : function(panelId) {
38587 var rs = this.regions;
38588 for(var target in rs){
38589 var r = rs[target];
38590 if(typeof r != "function"){
38591 if(r.hasPanel(panelId)){
38592 return r.showPanel(panelId);
38600 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38601 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38604 restoreState : function(provider){
38606 provider = Roo.state.Manager;
38608 var sm = new Roo.LayoutStateManager();
38609 sm.init(this, provider);
38615 * Adds a xtype elements to the layout.
38619 xtype : 'ContentPanel',
38626 xtype : 'NestedLayoutPanel',
38632 items : [ ... list of content panels or nested layout panels.. ]
38636 * @param {Object} cfg Xtype definition of item to add.
38638 addxtype : function(cfg)
38640 // basically accepts a pannel...
38641 // can accept a layout region..!?!?
38642 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38645 // theory? children can only be panels??
38647 //if (!cfg.xtype.match(/Panel$/)) {
38652 if (typeof(cfg.region) == 'undefined') {
38653 Roo.log("Failed to add Panel, region was not set");
38657 var region = cfg.region;
38663 xitems = cfg.items;
38668 if ( region == 'center') {
38669 Roo.log("Center: " + cfg.title);
38675 case 'Content': // ContentPanel (el, cfg)
38676 case 'Scroll': // ContentPanel (el, cfg)
38678 cfg.autoCreate = cfg.autoCreate || true;
38679 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38681 // var el = this.el.createChild();
38682 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38685 this.add(region, ret);
38689 case 'TreePanel': // our new panel!
38690 cfg.el = this.el.createChild();
38691 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38692 this.add(region, ret);
38697 // create a new Layout (which is a Border Layout...
38699 var clayout = cfg.layout;
38700 clayout.el = this.el.createChild();
38701 clayout.items = clayout.items || [];
38705 // replace this exitems with the clayout ones..
38706 xitems = clayout.items;
38708 // force background off if it's in center...
38709 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38710 cfg.background = false;
38712 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38715 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38716 //console.log('adding nested layout panel ' + cfg.toSource());
38717 this.add(region, ret);
38718 nb = {}; /// find first...
38723 // needs grid and region
38725 //var el = this.getRegion(region).el.createChild();
38727 *var el = this.el.createChild();
38728 // create the grid first...
38729 cfg.grid.container = el;
38730 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38733 if (region == 'center' && this.active ) {
38734 cfg.background = false;
38737 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38739 this.add(region, ret);
38741 if (cfg.background) {
38742 // render grid on panel activation (if panel background)
38743 ret.on('activate', function(gp) {
38744 if (!gp.grid.rendered) {
38745 // gp.grid.render(el);
38749 // cfg.grid.render(el);
38755 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38756 // it was the old xcomponent building that caused this before.
38757 // espeically if border is the top element in the tree.
38767 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38769 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38770 this.add(region, ret);
38774 throw "Can not add '" + cfg.xtype + "' to Border";
38780 this.beginUpdate();
38784 Roo.each(xitems, function(i) {
38785 region = nb && i.region ? i.region : false;
38787 var add = ret.addxtype(i);
38790 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38791 if (!i.background) {
38792 abn[region] = nb[region] ;
38799 // make the last non-background panel active..
38800 //if (nb) { Roo.log(abn); }
38803 for(var r in abn) {
38804 region = this.getRegion(r);
38806 // tried using nb[r], but it does not work..
38808 region.showPanel(abn[r]);
38819 factory : function(cfg)
38822 var validRegions = Roo.bootstrap.layout.Border.regions;
38824 var target = cfg.region;
38827 var r = Roo.bootstrap.layout;
38831 return new r.North(cfg);
38833 return new r.South(cfg);
38835 return new r.East(cfg);
38837 return new r.West(cfg);
38839 return new r.Center(cfg);
38841 throw 'Layout region "'+target+'" not supported.';
38848 * Ext JS Library 1.1.1
38849 * Copyright(c) 2006-2007, Ext JS, LLC.
38851 * Originally Released Under LGPL - original licence link has changed is not relivant.
38854 * <script type="text/javascript">
38858 * @class Roo.bootstrap.layout.Basic
38859 * @extends Roo.util.Observable
38860 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38861 * and does not have a titlebar, tabs or any other features. All it does is size and position
38862 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38863 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38864 * @cfg {string} region the region that it inhabits..
38865 * @cfg {bool} skipConfig skip config?
38869 Roo.bootstrap.layout.Basic = function(config){
38871 this.mgr = config.mgr;
38873 this.position = config.region;
38875 var skipConfig = config.skipConfig;
38879 * @scope Roo.BasicLayoutRegion
38883 * @event beforeremove
38884 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38885 * @param {Roo.LayoutRegion} this
38886 * @param {Roo.ContentPanel} panel The panel
38887 * @param {Object} e The cancel event object
38889 "beforeremove" : true,
38891 * @event invalidated
38892 * Fires when the layout for this region is changed.
38893 * @param {Roo.LayoutRegion} this
38895 "invalidated" : true,
38897 * @event visibilitychange
38898 * Fires when this region is shown or hidden
38899 * @param {Roo.LayoutRegion} this
38900 * @param {Boolean} visibility true or false
38902 "visibilitychange" : true,
38904 * @event paneladded
38905 * Fires when a panel is added.
38906 * @param {Roo.LayoutRegion} this
38907 * @param {Roo.ContentPanel} panel The panel
38909 "paneladded" : true,
38911 * @event panelremoved
38912 * Fires when a panel is removed.
38913 * @param {Roo.LayoutRegion} this
38914 * @param {Roo.ContentPanel} panel The panel
38916 "panelremoved" : true,
38918 * @event beforecollapse
38919 * Fires when this region before collapse.
38920 * @param {Roo.LayoutRegion} this
38922 "beforecollapse" : true,
38925 * Fires when this region is collapsed.
38926 * @param {Roo.LayoutRegion} this
38928 "collapsed" : true,
38931 * Fires when this region is expanded.
38932 * @param {Roo.LayoutRegion} this
38937 * Fires when this region is slid into view.
38938 * @param {Roo.LayoutRegion} this
38940 "slideshow" : true,
38943 * Fires when this region slides out of view.
38944 * @param {Roo.LayoutRegion} this
38946 "slidehide" : true,
38948 * @event panelactivated
38949 * Fires when a panel is activated.
38950 * @param {Roo.LayoutRegion} this
38951 * @param {Roo.ContentPanel} panel The activated panel
38953 "panelactivated" : true,
38956 * Fires when the user resizes this region.
38957 * @param {Roo.LayoutRegion} this
38958 * @param {Number} newSize The new size (width for east/west, height for north/south)
38962 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38963 this.panels = new Roo.util.MixedCollection();
38964 this.panels.getKey = this.getPanelId.createDelegate(this);
38966 this.activePanel = null;
38967 // ensure listeners are added...
38969 if (config.listeners || config.events) {
38970 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38971 listeners : config.listeners || {},
38972 events : config.events || {}
38976 if(skipConfig !== true){
38977 this.applyConfig(config);
38981 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38983 getPanelId : function(p){
38987 applyConfig : function(config){
38988 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38989 this.config = config;
38994 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38995 * the width, for horizontal (north, south) the height.
38996 * @param {Number} newSize The new width or height
38998 resizeTo : function(newSize){
38999 var el = this.el ? this.el :
39000 (this.activePanel ? this.activePanel.getEl() : null);
39002 switch(this.position){
39005 el.setWidth(newSize);
39006 this.fireEvent("resized", this, newSize);
39010 el.setHeight(newSize);
39011 this.fireEvent("resized", this, newSize);
39017 getBox : function(){
39018 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39021 getMargins : function(){
39022 return this.margins;
39025 updateBox : function(box){
39027 var el = this.activePanel.getEl();
39028 el.dom.style.left = box.x + "px";
39029 el.dom.style.top = box.y + "px";
39030 this.activePanel.setSize(box.width, box.height);
39034 * Returns the container element for this region.
39035 * @return {Roo.Element}
39037 getEl : function(){
39038 return this.activePanel;
39042 * Returns true if this region is currently visible.
39043 * @return {Boolean}
39045 isVisible : function(){
39046 return this.activePanel ? true : false;
39049 setActivePanel : function(panel){
39050 panel = this.getPanel(panel);
39051 if(this.activePanel && this.activePanel != panel){
39052 this.activePanel.setActiveState(false);
39053 this.activePanel.getEl().setLeftTop(-10000,-10000);
39055 this.activePanel = panel;
39056 panel.setActiveState(true);
39058 panel.setSize(this.box.width, this.box.height);
39060 this.fireEvent("panelactivated", this, panel);
39061 this.fireEvent("invalidated");
39065 * Show the specified panel.
39066 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39067 * @return {Roo.ContentPanel} The shown panel or null
39069 showPanel : function(panel){
39070 panel = this.getPanel(panel);
39072 this.setActivePanel(panel);
39078 * Get the active panel for this region.
39079 * @return {Roo.ContentPanel} The active panel or null
39081 getActivePanel : function(){
39082 return this.activePanel;
39086 * Add the passed ContentPanel(s)
39087 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39088 * @return {Roo.ContentPanel} The panel added (if only one was added)
39090 add : function(panel){
39091 if(arguments.length > 1){
39092 for(var i = 0, len = arguments.length; i < len; i++) {
39093 this.add(arguments[i]);
39097 if(this.hasPanel(panel)){
39098 this.showPanel(panel);
39101 var el = panel.getEl();
39102 if(el.dom.parentNode != this.mgr.el.dom){
39103 this.mgr.el.dom.appendChild(el.dom);
39105 if(panel.setRegion){
39106 panel.setRegion(this);
39108 this.panels.add(panel);
39109 el.setStyle("position", "absolute");
39110 if(!panel.background){
39111 this.setActivePanel(panel);
39112 if(this.config.initialSize && this.panels.getCount()==1){
39113 this.resizeTo(this.config.initialSize);
39116 this.fireEvent("paneladded", this, panel);
39121 * Returns true if the panel is in this region.
39122 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39123 * @return {Boolean}
39125 hasPanel : function(panel){
39126 if(typeof panel == "object"){ // must be panel obj
39127 panel = panel.getId();
39129 return this.getPanel(panel) ? true : false;
39133 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39134 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39135 * @param {Boolean} preservePanel Overrides the config preservePanel option
39136 * @return {Roo.ContentPanel} The panel that was removed
39138 remove : function(panel, preservePanel){
39139 panel = this.getPanel(panel);
39144 this.fireEvent("beforeremove", this, panel, e);
39145 if(e.cancel === true){
39148 var panelId = panel.getId();
39149 this.panels.removeKey(panelId);
39154 * Returns the panel specified or null if it's not in this region.
39155 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39156 * @return {Roo.ContentPanel}
39158 getPanel : function(id){
39159 if(typeof id == "object"){ // must be panel obj
39162 return this.panels.get(id);
39166 * Returns this regions position (north/south/east/west/center).
39169 getPosition: function(){
39170 return this.position;
39174 * Ext JS Library 1.1.1
39175 * Copyright(c) 2006-2007, Ext JS, LLC.
39177 * Originally Released Under LGPL - original licence link has changed is not relivant.
39180 * <script type="text/javascript">
39184 * @class Roo.bootstrap.layout.Region
39185 * @extends Roo.bootstrap.layout.Basic
39186 * This class represents a region in a layout manager.
39188 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39189 * @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})
39190 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39191 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39192 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39193 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39194 * @cfg {String} title The title for the region (overrides panel titles)
39195 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39196 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39197 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39198 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39199 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39200 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39201 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39202 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39203 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39204 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39206 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39207 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39208 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39209 * @cfg {Number} width For East/West panels
39210 * @cfg {Number} height For North/South panels
39211 * @cfg {Boolean} split To show the splitter
39212 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39214 * @cfg {string} cls Extra CSS classes to add to region
39216 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39217 * @cfg {string} region the region that it inhabits..
39220 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39221 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39223 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39224 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39225 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39227 Roo.bootstrap.layout.Region = function(config)
39229 this.applyConfig(config);
39231 var mgr = config.mgr;
39232 var pos = config.region;
39233 config.skipConfig = true;
39234 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39237 this.onRender(mgr.el);
39240 this.visible = true;
39241 this.collapsed = false;
39242 this.unrendered_panels = [];
39245 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39247 position: '', // set by wrapper (eg. north/south etc..)
39248 unrendered_panels : null, // unrendered panels.
39250 tabPosition : false,
39252 mgr: false, // points to 'Border'
39255 createBody : function(){
39256 /** This region's body element
39257 * @type Roo.Element */
39258 this.bodyEl = this.el.createChild({
39260 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39264 onRender: function(ctr, pos)
39266 var dh = Roo.DomHelper;
39267 /** This region's container element
39268 * @type Roo.Element */
39269 this.el = dh.append(ctr.dom, {
39271 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39273 /** This region's title element
39274 * @type Roo.Element */
39276 this.titleEl = dh.append(this.el.dom, {
39278 unselectable: "on",
39279 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39281 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39282 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39286 this.titleEl.enableDisplayMode();
39287 /** This region's title text element
39288 * @type HTMLElement */
39289 this.titleTextEl = this.titleEl.dom.firstChild;
39290 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39292 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39293 this.closeBtn.enableDisplayMode();
39294 this.closeBtn.on("click", this.closeClicked, this);
39295 this.closeBtn.hide();
39297 this.createBody(this.config);
39298 if(this.config.hideWhenEmpty){
39300 this.on("paneladded", this.validateVisibility, this);
39301 this.on("panelremoved", this.validateVisibility, this);
39303 if(this.autoScroll){
39304 this.bodyEl.setStyle("overflow", "auto");
39306 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39308 //if(c.titlebar !== false){
39309 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39310 this.titleEl.hide();
39312 this.titleEl.show();
39313 if(this.config.title){
39314 this.titleTextEl.innerHTML = this.config.title;
39318 if(this.config.collapsed){
39319 this.collapse(true);
39321 if(this.config.hidden){
39325 if (this.unrendered_panels && this.unrendered_panels.length) {
39326 for (var i =0;i< this.unrendered_panels.length; i++) {
39327 this.add(this.unrendered_panels[i]);
39329 this.unrendered_panels = null;
39335 applyConfig : function(c)
39338 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39339 var dh = Roo.DomHelper;
39340 if(c.titlebar !== false){
39341 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39342 this.collapseBtn.on("click", this.collapse, this);
39343 this.collapseBtn.enableDisplayMode();
39345 if(c.showPin === true || this.showPin){
39346 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39347 this.stickBtn.enableDisplayMode();
39348 this.stickBtn.on("click", this.expand, this);
39349 this.stickBtn.hide();
39354 /** This region's collapsed element
39355 * @type Roo.Element */
39358 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39359 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39362 if(c.floatable !== false){
39363 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39364 this.collapsedEl.on("click", this.collapseClick, this);
39367 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39368 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39369 id: "message", unselectable: "on", style:{"float":"left"}});
39370 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39372 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39373 this.expandBtn.on("click", this.expand, this);
39377 if(this.collapseBtn){
39378 this.collapseBtn.setVisible(c.collapsible == true);
39381 this.cmargins = c.cmargins || this.cmargins ||
39382 (this.position == "west" || this.position == "east" ?
39383 {top: 0, left: 2, right:2, bottom: 0} :
39384 {top: 2, left: 0, right:0, bottom: 2});
39386 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39389 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39391 this.autoScroll = c.autoScroll || false;
39396 this.duration = c.duration || .30;
39397 this.slideDuration = c.slideDuration || .45;
39402 * Returns true if this region is currently visible.
39403 * @return {Boolean}
39405 isVisible : function(){
39406 return this.visible;
39410 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39411 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39413 //setCollapsedTitle : function(title){
39414 // title = title || " ";
39415 // if(this.collapsedTitleTextEl){
39416 // this.collapsedTitleTextEl.innerHTML = title;
39420 getBox : function(){
39422 // if(!this.collapsed){
39423 b = this.el.getBox(false, true);
39425 // b = this.collapsedEl.getBox(false, true);
39430 getMargins : function(){
39431 return this.margins;
39432 //return this.collapsed ? this.cmargins : this.margins;
39435 highlight : function(){
39436 this.el.addClass("x-layout-panel-dragover");
39439 unhighlight : function(){
39440 this.el.removeClass("x-layout-panel-dragover");
39443 updateBox : function(box)
39445 if (!this.bodyEl) {
39446 return; // not rendered yet..
39450 if(!this.collapsed){
39451 this.el.dom.style.left = box.x + "px";
39452 this.el.dom.style.top = box.y + "px";
39453 this.updateBody(box.width, box.height);
39455 this.collapsedEl.dom.style.left = box.x + "px";
39456 this.collapsedEl.dom.style.top = box.y + "px";
39457 this.collapsedEl.setSize(box.width, box.height);
39460 this.tabs.autoSizeTabs();
39464 updateBody : function(w, h)
39467 this.el.setWidth(w);
39468 w -= this.el.getBorderWidth("rl");
39469 if(this.config.adjustments){
39470 w += this.config.adjustments[0];
39473 if(h !== null && h > 0){
39474 this.el.setHeight(h);
39475 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39476 h -= this.el.getBorderWidth("tb");
39477 if(this.config.adjustments){
39478 h += this.config.adjustments[1];
39480 this.bodyEl.setHeight(h);
39482 h = this.tabs.syncHeight(h);
39485 if(this.panelSize){
39486 w = w !== null ? w : this.panelSize.width;
39487 h = h !== null ? h : this.panelSize.height;
39489 if(this.activePanel){
39490 var el = this.activePanel.getEl();
39491 w = w !== null ? w : el.getWidth();
39492 h = h !== null ? h : el.getHeight();
39493 this.panelSize = {width: w, height: h};
39494 this.activePanel.setSize(w, h);
39496 if(Roo.isIE && this.tabs){
39497 this.tabs.el.repaint();
39502 * Returns the container element for this region.
39503 * @return {Roo.Element}
39505 getEl : function(){
39510 * Hides this region.
39513 //if(!this.collapsed){
39514 this.el.dom.style.left = "-2000px";
39517 // this.collapsedEl.dom.style.left = "-2000px";
39518 // this.collapsedEl.hide();
39520 this.visible = false;
39521 this.fireEvent("visibilitychange", this, false);
39525 * Shows this region if it was previously hidden.
39528 //if(!this.collapsed){
39531 // this.collapsedEl.show();
39533 this.visible = true;
39534 this.fireEvent("visibilitychange", this, true);
39537 closeClicked : function(){
39538 if(this.activePanel){
39539 this.remove(this.activePanel);
39543 collapseClick : function(e){
39545 e.stopPropagation();
39548 e.stopPropagation();
39554 * Collapses this region.
39555 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39558 collapse : function(skipAnim, skipCheck = false){
39559 if(this.collapsed) {
39563 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39565 this.collapsed = true;
39567 this.split.el.hide();
39569 if(this.config.animate && skipAnim !== true){
39570 this.fireEvent("invalidated", this);
39571 this.animateCollapse();
39573 this.el.setLocation(-20000,-20000);
39575 this.collapsedEl.show();
39576 this.fireEvent("collapsed", this);
39577 this.fireEvent("invalidated", this);
39583 animateCollapse : function(){
39588 * Expands this region if it was previously collapsed.
39589 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39590 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39593 expand : function(e, skipAnim){
39595 e.stopPropagation();
39597 if(!this.collapsed || this.el.hasActiveFx()) {
39601 this.afterSlideIn();
39604 this.collapsed = false;
39605 if(this.config.animate && skipAnim !== true){
39606 this.animateExpand();
39610 this.split.el.show();
39612 this.collapsedEl.setLocation(-2000,-2000);
39613 this.collapsedEl.hide();
39614 this.fireEvent("invalidated", this);
39615 this.fireEvent("expanded", this);
39619 animateExpand : function(){
39623 initTabs : function()
39625 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39627 var ts = new Roo.bootstrap.panel.Tabs({
39628 el: this.bodyEl.dom,
39630 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39631 disableTooltips: this.config.disableTabTips,
39632 toolbar : this.config.toolbar
39635 if(this.config.hideTabs){
39636 ts.stripWrap.setDisplayed(false);
39639 ts.resizeTabs = this.config.resizeTabs === true;
39640 ts.minTabWidth = this.config.minTabWidth || 40;
39641 ts.maxTabWidth = this.config.maxTabWidth || 250;
39642 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39643 ts.monitorResize = false;
39644 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39645 ts.bodyEl.addClass('roo-layout-tabs-body');
39646 this.panels.each(this.initPanelAsTab, this);
39649 initPanelAsTab : function(panel){
39650 var ti = this.tabs.addTab(
39654 this.config.closeOnTab && panel.isClosable(),
39657 if(panel.tabTip !== undefined){
39658 ti.setTooltip(panel.tabTip);
39660 ti.on("activate", function(){
39661 this.setActivePanel(panel);
39664 if(this.config.closeOnTab){
39665 ti.on("beforeclose", function(t, e){
39667 this.remove(panel);
39671 panel.tabItem = ti;
39676 updatePanelTitle : function(panel, title)
39678 if(this.activePanel == panel){
39679 this.updateTitle(title);
39682 var ti = this.tabs.getTab(panel.getEl().id);
39684 if(panel.tabTip !== undefined){
39685 ti.setTooltip(panel.tabTip);
39690 updateTitle : function(title){
39691 if(this.titleTextEl && !this.config.title){
39692 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39696 setActivePanel : function(panel)
39698 panel = this.getPanel(panel);
39699 if(this.activePanel && this.activePanel != panel){
39700 if(this.activePanel.setActiveState(false) === false){
39704 this.activePanel = panel;
39705 panel.setActiveState(true);
39706 if(this.panelSize){
39707 panel.setSize(this.panelSize.width, this.panelSize.height);
39710 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39712 this.updateTitle(panel.getTitle());
39714 this.fireEvent("invalidated", this);
39716 this.fireEvent("panelactivated", this, panel);
39720 * Shows the specified panel.
39721 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39722 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39724 showPanel : function(panel)
39726 panel = this.getPanel(panel);
39729 var tab = this.tabs.getTab(panel.getEl().id);
39730 if(tab.isHidden()){
39731 this.tabs.unhideTab(tab.id);
39735 this.setActivePanel(panel);
39742 * Get the active panel for this region.
39743 * @return {Roo.ContentPanel} The active panel or null
39745 getActivePanel : function(){
39746 return this.activePanel;
39749 validateVisibility : function(){
39750 if(this.panels.getCount() < 1){
39751 this.updateTitle(" ");
39752 this.closeBtn.hide();
39755 if(!this.isVisible()){
39762 * Adds the passed ContentPanel(s) to this region.
39763 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39764 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39766 add : function(panel)
39768 if(arguments.length > 1){
39769 for(var i = 0, len = arguments.length; i < len; i++) {
39770 this.add(arguments[i]);
39775 // if we have not been rendered yet, then we can not really do much of this..
39776 if (!this.bodyEl) {
39777 this.unrendered_panels.push(panel);
39784 if(this.hasPanel(panel)){
39785 this.showPanel(panel);
39788 panel.setRegion(this);
39789 this.panels.add(panel);
39790 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39791 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39792 // and hide them... ???
39793 this.bodyEl.dom.appendChild(panel.getEl().dom);
39794 if(panel.background !== true){
39795 this.setActivePanel(panel);
39797 this.fireEvent("paneladded", this, panel);
39804 this.initPanelAsTab(panel);
39808 if(panel.background !== true){
39809 this.tabs.activate(panel.getEl().id);
39811 this.fireEvent("paneladded", this, panel);
39816 * Hides the tab for the specified panel.
39817 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39819 hidePanel : function(panel){
39820 if(this.tabs && (panel = this.getPanel(panel))){
39821 this.tabs.hideTab(panel.getEl().id);
39826 * Unhides the tab for a previously hidden panel.
39827 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39829 unhidePanel : function(panel){
39830 if(this.tabs && (panel = this.getPanel(panel))){
39831 this.tabs.unhideTab(panel.getEl().id);
39835 clearPanels : function(){
39836 while(this.panels.getCount() > 0){
39837 this.remove(this.panels.first());
39842 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39843 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39844 * @param {Boolean} preservePanel Overrides the config preservePanel option
39845 * @return {Roo.ContentPanel} The panel that was removed
39847 remove : function(panel, preservePanel)
39849 panel = this.getPanel(panel);
39854 this.fireEvent("beforeremove", this, panel, e);
39855 if(e.cancel === true){
39858 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39859 var panelId = panel.getId();
39860 this.panels.removeKey(panelId);
39862 document.body.appendChild(panel.getEl().dom);
39865 this.tabs.removeTab(panel.getEl().id);
39866 }else if (!preservePanel){
39867 this.bodyEl.dom.removeChild(panel.getEl().dom);
39869 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39870 var p = this.panels.first();
39871 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39872 tempEl.appendChild(p.getEl().dom);
39873 this.bodyEl.update("");
39874 this.bodyEl.dom.appendChild(p.getEl().dom);
39876 this.updateTitle(p.getTitle());
39878 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39879 this.setActivePanel(p);
39881 panel.setRegion(null);
39882 if(this.activePanel == panel){
39883 this.activePanel = null;
39885 if(this.config.autoDestroy !== false && preservePanel !== true){
39886 try{panel.destroy();}catch(e){}
39888 this.fireEvent("panelremoved", this, panel);
39893 * Returns the TabPanel component used by this region
39894 * @return {Roo.TabPanel}
39896 getTabs : function(){
39900 createTool : function(parentEl, className){
39901 var btn = Roo.DomHelper.append(parentEl, {
39903 cls: "x-layout-tools-button",
39906 cls: "roo-layout-tools-button-inner " + className,
39910 btn.addClassOnOver("roo-layout-tools-button-over");
39915 * Ext JS Library 1.1.1
39916 * Copyright(c) 2006-2007, Ext JS, LLC.
39918 * Originally Released Under LGPL - original licence link has changed is not relivant.
39921 * <script type="text/javascript">
39927 * @class Roo.SplitLayoutRegion
39928 * @extends Roo.LayoutRegion
39929 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39931 Roo.bootstrap.layout.Split = function(config){
39932 this.cursor = config.cursor;
39933 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39936 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39938 splitTip : "Drag to resize.",
39939 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39940 useSplitTips : false,
39942 applyConfig : function(config){
39943 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39946 onRender : function(ctr,pos) {
39948 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39949 if(!this.config.split){
39954 var splitEl = Roo.DomHelper.append(ctr.dom, {
39956 id: this.el.id + "-split",
39957 cls: "roo-layout-split roo-layout-split-"+this.position,
39960 /** The SplitBar for this region
39961 * @type Roo.SplitBar */
39962 // does not exist yet...
39963 Roo.log([this.position, this.orientation]);
39965 this.split = new Roo.bootstrap.SplitBar({
39966 dragElement : splitEl,
39967 resizingElement: this.el,
39968 orientation : this.orientation
39971 this.split.on("moved", this.onSplitMove, this);
39972 this.split.useShim = this.config.useShim === true;
39973 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39974 if(this.useSplitTips){
39975 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39977 //if(config.collapsible){
39978 // this.split.el.on("dblclick", this.collapse, this);
39981 if(typeof this.config.minSize != "undefined"){
39982 this.split.minSize = this.config.minSize;
39984 if(typeof this.config.maxSize != "undefined"){
39985 this.split.maxSize = this.config.maxSize;
39987 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39988 this.hideSplitter();
39993 getHMaxSize : function(){
39994 var cmax = this.config.maxSize || 10000;
39995 var center = this.mgr.getRegion("center");
39996 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39999 getVMaxSize : function(){
40000 var cmax = this.config.maxSize || 10000;
40001 var center = this.mgr.getRegion("center");
40002 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40005 onSplitMove : function(split, newSize){
40006 this.fireEvent("resized", this, newSize);
40010 * Returns the {@link Roo.SplitBar} for this region.
40011 * @return {Roo.SplitBar}
40013 getSplitBar : function(){
40018 this.hideSplitter();
40019 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40022 hideSplitter : function(){
40024 this.split.el.setLocation(-2000,-2000);
40025 this.split.el.hide();
40031 this.split.el.show();
40033 Roo.bootstrap.layout.Split.superclass.show.call(this);
40036 beforeSlide: function(){
40037 if(Roo.isGecko){// firefox overflow auto bug workaround
40038 this.bodyEl.clip();
40040 this.tabs.bodyEl.clip();
40042 if(this.activePanel){
40043 this.activePanel.getEl().clip();
40045 if(this.activePanel.beforeSlide){
40046 this.activePanel.beforeSlide();
40052 afterSlide : function(){
40053 if(Roo.isGecko){// firefox overflow auto bug workaround
40054 this.bodyEl.unclip();
40056 this.tabs.bodyEl.unclip();
40058 if(this.activePanel){
40059 this.activePanel.getEl().unclip();
40060 if(this.activePanel.afterSlide){
40061 this.activePanel.afterSlide();
40067 initAutoHide : function(){
40068 if(this.autoHide !== false){
40069 if(!this.autoHideHd){
40070 var st = new Roo.util.DelayedTask(this.slideIn, this);
40071 this.autoHideHd = {
40072 "mouseout": function(e){
40073 if(!e.within(this.el, true)){
40077 "mouseover" : function(e){
40083 this.el.on(this.autoHideHd);
40087 clearAutoHide : function(){
40088 if(this.autoHide !== false){
40089 this.el.un("mouseout", this.autoHideHd.mouseout);
40090 this.el.un("mouseover", this.autoHideHd.mouseover);
40094 clearMonitor : function(){
40095 Roo.get(document).un("click", this.slideInIf, this);
40098 // these names are backwards but not changed for compat
40099 slideOut : function(){
40100 if(this.isSlid || this.el.hasActiveFx()){
40103 this.isSlid = true;
40104 if(this.collapseBtn){
40105 this.collapseBtn.hide();
40107 this.closeBtnState = this.closeBtn.getStyle('display');
40108 this.closeBtn.hide();
40110 this.stickBtn.show();
40113 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40114 this.beforeSlide();
40115 this.el.setStyle("z-index", 10001);
40116 this.el.slideIn(this.getSlideAnchor(), {
40117 callback: function(){
40119 this.initAutoHide();
40120 Roo.get(document).on("click", this.slideInIf, this);
40121 this.fireEvent("slideshow", this);
40128 afterSlideIn : function(){
40129 this.clearAutoHide();
40130 this.isSlid = false;
40131 this.clearMonitor();
40132 this.el.setStyle("z-index", "");
40133 if(this.collapseBtn){
40134 this.collapseBtn.show();
40136 this.closeBtn.setStyle('display', this.closeBtnState);
40138 this.stickBtn.hide();
40140 this.fireEvent("slidehide", this);
40143 slideIn : function(cb){
40144 if(!this.isSlid || this.el.hasActiveFx()){
40148 this.isSlid = false;
40149 this.beforeSlide();
40150 this.el.slideOut(this.getSlideAnchor(), {
40151 callback: function(){
40152 this.el.setLeftTop(-10000, -10000);
40154 this.afterSlideIn();
40162 slideInIf : function(e){
40163 if(!e.within(this.el)){
40168 animateCollapse : function(){
40169 this.beforeSlide();
40170 this.el.setStyle("z-index", 20000);
40171 var anchor = this.getSlideAnchor();
40172 this.el.slideOut(anchor, {
40173 callback : function(){
40174 this.el.setStyle("z-index", "");
40175 this.collapsedEl.slideIn(anchor, {duration:.3});
40177 this.el.setLocation(-10000,-10000);
40179 this.fireEvent("collapsed", this);
40186 animateExpand : function(){
40187 this.beforeSlide();
40188 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40189 this.el.setStyle("z-index", 20000);
40190 this.collapsedEl.hide({
40193 this.el.slideIn(this.getSlideAnchor(), {
40194 callback : function(){
40195 this.el.setStyle("z-index", "");
40198 this.split.el.show();
40200 this.fireEvent("invalidated", this);
40201 this.fireEvent("expanded", this);
40229 getAnchor : function(){
40230 return this.anchors[this.position];
40233 getCollapseAnchor : function(){
40234 return this.canchors[this.position];
40237 getSlideAnchor : function(){
40238 return this.sanchors[this.position];
40241 getAlignAdj : function(){
40242 var cm = this.cmargins;
40243 switch(this.position){
40259 getExpandAdj : function(){
40260 var c = this.collapsedEl, cm = this.cmargins;
40261 switch(this.position){
40263 return [-(cm.right+c.getWidth()+cm.left), 0];
40266 return [cm.right+c.getWidth()+cm.left, 0];
40269 return [0, -(cm.top+cm.bottom+c.getHeight())];
40272 return [0, cm.top+cm.bottom+c.getHeight()];
40278 * Ext JS Library 1.1.1
40279 * Copyright(c) 2006-2007, Ext JS, LLC.
40281 * Originally Released Under LGPL - original licence link has changed is not relivant.
40284 * <script type="text/javascript">
40287 * These classes are private internal classes
40289 Roo.bootstrap.layout.Center = function(config){
40290 config.region = "center";
40291 Roo.bootstrap.layout.Region.call(this, config);
40292 this.visible = true;
40293 this.minWidth = config.minWidth || 20;
40294 this.minHeight = config.minHeight || 20;
40297 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40299 // center panel can't be hidden
40303 // center panel can't be hidden
40306 getMinWidth: function(){
40307 return this.minWidth;
40310 getMinHeight: function(){
40311 return this.minHeight;
40325 Roo.bootstrap.layout.North = function(config)
40327 config.region = 'north';
40328 config.cursor = 'n-resize';
40330 Roo.bootstrap.layout.Split.call(this, config);
40334 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40335 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40336 this.split.el.addClass("roo-layout-split-v");
40338 //var size = config.initialSize || config.height;
40339 //if(this.el && typeof size != "undefined"){
40340 // this.el.setHeight(size);
40343 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40345 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40348 onRender : function(ctr, pos)
40350 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40351 var size = this.config.initialSize || this.config.height;
40352 if(this.el && typeof size != "undefined"){
40353 this.el.setHeight(size);
40358 getBox : function(){
40359 if(this.collapsed){
40360 return this.collapsedEl.getBox();
40362 var box = this.el.getBox();
40364 box.height += this.split.el.getHeight();
40369 updateBox : function(box){
40370 if(this.split && !this.collapsed){
40371 box.height -= this.split.el.getHeight();
40372 this.split.el.setLeft(box.x);
40373 this.split.el.setTop(box.y+box.height);
40374 this.split.el.setWidth(box.width);
40376 if(this.collapsed){
40377 this.updateBody(box.width, null);
40379 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40387 Roo.bootstrap.layout.South = function(config){
40388 config.region = 'south';
40389 config.cursor = 's-resize';
40390 Roo.bootstrap.layout.Split.call(this, config);
40392 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40393 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40394 this.split.el.addClass("roo-layout-split-v");
40399 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40400 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40402 onRender : function(ctr, pos)
40404 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40405 var size = this.config.initialSize || this.config.height;
40406 if(this.el && typeof size != "undefined"){
40407 this.el.setHeight(size);
40412 getBox : function(){
40413 if(this.collapsed){
40414 return this.collapsedEl.getBox();
40416 var box = this.el.getBox();
40418 var sh = this.split.el.getHeight();
40425 updateBox : function(box){
40426 if(this.split && !this.collapsed){
40427 var sh = this.split.el.getHeight();
40430 this.split.el.setLeft(box.x);
40431 this.split.el.setTop(box.y-sh);
40432 this.split.el.setWidth(box.width);
40434 if(this.collapsed){
40435 this.updateBody(box.width, null);
40437 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40441 Roo.bootstrap.layout.East = function(config){
40442 config.region = "east";
40443 config.cursor = "e-resize";
40444 Roo.bootstrap.layout.Split.call(this, config);
40446 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40447 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40448 this.split.el.addClass("roo-layout-split-h");
40452 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40453 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40455 onRender : function(ctr, pos)
40457 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40458 var size = this.config.initialSize || this.config.width;
40459 if(this.el && typeof size != "undefined"){
40460 this.el.setWidth(size);
40465 getBox : function(){
40466 if(this.collapsed){
40467 return this.collapsedEl.getBox();
40469 var box = this.el.getBox();
40471 var sw = this.split.el.getWidth();
40478 updateBox : function(box){
40479 if(this.split && !this.collapsed){
40480 var sw = this.split.el.getWidth();
40482 this.split.el.setLeft(box.x);
40483 this.split.el.setTop(box.y);
40484 this.split.el.setHeight(box.height);
40487 if(this.collapsed){
40488 this.updateBody(null, box.height);
40490 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40494 Roo.bootstrap.layout.West = function(config){
40495 config.region = "west";
40496 config.cursor = "w-resize";
40498 Roo.bootstrap.layout.Split.call(this, config);
40500 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40501 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40502 this.split.el.addClass("roo-layout-split-h");
40506 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40507 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40509 onRender: function(ctr, pos)
40511 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40512 var size = this.config.initialSize || this.config.width;
40513 if(typeof size != "undefined"){
40514 this.el.setWidth(size);
40518 getBox : function(){
40519 if(this.collapsed){
40520 return this.collapsedEl.getBox();
40522 var box = this.el.getBox();
40523 if (box.width == 0) {
40524 box.width = this.config.width; // kludge?
40527 box.width += this.split.el.getWidth();
40532 updateBox : function(box){
40533 if(this.split && !this.collapsed){
40534 var sw = this.split.el.getWidth();
40536 this.split.el.setLeft(box.x+box.width);
40537 this.split.el.setTop(box.y);
40538 this.split.el.setHeight(box.height);
40540 if(this.collapsed){
40541 this.updateBody(null, box.height);
40543 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40545 });Roo.namespace("Roo.bootstrap.panel");/*
40547 * Ext JS Library 1.1.1
40548 * Copyright(c) 2006-2007, Ext JS, LLC.
40550 * Originally Released Under LGPL - original licence link has changed is not relivant.
40553 * <script type="text/javascript">
40556 * @class Roo.ContentPanel
40557 * @extends Roo.util.Observable
40559 * A basic ContentPanel element.
40560 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40561 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40562 * @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
40563 * @cfg {Boolean} closable True if the panel can be closed/removed
40564 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40565 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40566 * @cfg {Toolbar} toolbar A toolbar for this panel
40567 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40568 * @cfg {String} title The title for this panel
40569 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40570 * @cfg {String} url Calls {@link #setUrl} with this value
40571 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40572 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40573 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40574 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40575 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40576 * @cfg {Boolean} badges render the badges
40577 * @cfg {String} cls extra classes to use
40578 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40581 * Create a new ContentPanel.
40582 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40583 * @param {String/Object} config A string to set only the title or a config object
40584 * @param {String} content (optional) Set the HTML content for this panel
40585 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40587 Roo.bootstrap.panel.Content = function( config){
40589 this.tpl = config.tpl || false;
40591 var el = config.el;
40592 var content = config.content;
40594 if(config.autoCreate){ // xtype is available if this is called from factory
40597 this.el = Roo.get(el);
40598 if(!this.el && config && config.autoCreate){
40599 if(typeof config.autoCreate == "object"){
40600 if(!config.autoCreate.id){
40601 config.autoCreate.id = config.id||el;
40603 this.el = Roo.DomHelper.append(document.body,
40604 config.autoCreate, true);
40608 cls: (config.cls || '') +
40609 (config.background ? ' bg-' + config.background : '') +
40610 " roo-layout-inactive-content",
40613 if (config.iframe) {
40617 style : 'border: 0px',
40618 src : 'about:blank'
40624 elcfg.html = config.html;
40628 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40629 if (config.iframe) {
40630 this.iframeEl = this.el.select('iframe',true).first();
40635 this.closable = false;
40636 this.loaded = false;
40637 this.active = false;
40640 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40642 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40644 this.wrapEl = this.el; //this.el.wrap();
40646 if (config.toolbar.items) {
40647 ti = config.toolbar.items ;
40648 delete config.toolbar.items ;
40652 this.toolbar.render(this.wrapEl, 'before');
40653 for(var i =0;i < ti.length;i++) {
40654 // Roo.log(['add child', items[i]]);
40655 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40657 this.toolbar.items = nitems;
40658 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40659 delete config.toolbar;
40663 // xtype created footer. - not sure if will work as we normally have to render first..
40664 if (this.footer && !this.footer.el && this.footer.xtype) {
40665 if (!this.wrapEl) {
40666 this.wrapEl = this.el.wrap();
40669 this.footer.container = this.wrapEl.createChild();
40671 this.footer = Roo.factory(this.footer, Roo);
40676 if(typeof config == "string"){
40677 this.title = config;
40679 Roo.apply(this, config);
40683 this.resizeEl = Roo.get(this.resizeEl, true);
40685 this.resizeEl = this.el;
40687 // handle view.xtype
40695 * Fires when this panel is activated.
40696 * @param {Roo.ContentPanel} this
40700 * @event deactivate
40701 * Fires when this panel is activated.
40702 * @param {Roo.ContentPanel} this
40704 "deactivate" : true,
40708 * Fires when this panel is resized if fitToFrame is true.
40709 * @param {Roo.ContentPanel} this
40710 * @param {Number} width The width after any component adjustments
40711 * @param {Number} height The height after any component adjustments
40717 * Fires when this tab is created
40718 * @param {Roo.ContentPanel} this
40724 * Fires when this content is scrolled
40725 * @param {Roo.ContentPanel} this
40726 * @param {Event} scrollEvent
40737 if(this.autoScroll && !this.iframe){
40738 this.resizeEl.setStyle("overflow", "auto");
40739 this.resizeEl.on('scroll', this.onScroll, this);
40741 // fix randome scrolling
40742 //this.el.on('scroll', function() {
40743 // Roo.log('fix random scolling');
40744 // this.scrollTo('top',0);
40747 content = content || this.content;
40749 this.setContent(content);
40751 if(config && config.url){
40752 this.setUrl(this.url, this.params, this.loadOnce);
40757 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40759 if (this.view && typeof(this.view.xtype) != 'undefined') {
40760 this.view.el = this.el.appendChild(document.createElement("div"));
40761 this.view = Roo.factory(this.view);
40762 this.view.render && this.view.render(false, '');
40766 this.fireEvent('render', this);
40769 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40779 /* Resize Element - use this to work out scroll etc. */
40782 setRegion : function(region){
40783 this.region = region;
40784 this.setActiveClass(region && !this.background);
40788 setActiveClass: function(state)
40791 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40792 this.el.setStyle('position','relative');
40794 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40795 this.el.setStyle('position', 'absolute');
40800 * Returns the toolbar for this Panel if one was configured.
40801 * @return {Roo.Toolbar}
40803 getToolbar : function(){
40804 return this.toolbar;
40807 setActiveState : function(active)
40809 this.active = active;
40810 this.setActiveClass(active);
40812 if(this.fireEvent("deactivate", this) === false){
40817 this.fireEvent("activate", this);
40821 * Updates this panel's element (not for iframe)
40822 * @param {String} content The new content
40823 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40825 setContent : function(content, loadScripts){
40830 this.el.update(content, loadScripts);
40833 ignoreResize : function(w, h){
40834 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40837 this.lastSize = {width: w, height: h};
40842 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40843 * @return {Roo.UpdateManager} The UpdateManager
40845 getUpdateManager : function(){
40849 return this.el.getUpdateManager();
40852 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40853 * Does not work with IFRAME contents
40854 * @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:
40857 url: "your-url.php",
40858 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40859 callback: yourFunction,
40860 scope: yourObject, //(optional scope)
40863 text: "Loading...",
40869 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40870 * 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.
40871 * @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}
40872 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40873 * @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.
40874 * @return {Roo.ContentPanel} this
40882 var um = this.el.getUpdateManager();
40883 um.update.apply(um, arguments);
40889 * 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.
40890 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40891 * @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)
40892 * @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)
40893 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40895 setUrl : function(url, params, loadOnce){
40897 this.iframeEl.dom.src = url;
40901 if(this.refreshDelegate){
40902 this.removeListener("activate", this.refreshDelegate);
40904 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40905 this.on("activate", this.refreshDelegate);
40906 return this.el.getUpdateManager();
40909 _handleRefresh : function(url, params, loadOnce){
40910 if(!loadOnce || !this.loaded){
40911 var updater = this.el.getUpdateManager();
40912 updater.update(url, params, this._setLoaded.createDelegate(this));
40916 _setLoaded : function(){
40917 this.loaded = true;
40921 * Returns this panel's id
40924 getId : function(){
40929 * Returns this panel's element - used by regiosn to add.
40930 * @return {Roo.Element}
40932 getEl : function(){
40933 return this.wrapEl || this.el;
40938 adjustForComponents : function(width, height)
40940 //Roo.log('adjustForComponents ');
40941 if(this.resizeEl != this.el){
40942 width -= this.el.getFrameWidth('lr');
40943 height -= this.el.getFrameWidth('tb');
40946 var te = this.toolbar.getEl();
40947 te.setWidth(width);
40948 height -= te.getHeight();
40951 var te = this.footer.getEl();
40952 te.setWidth(width);
40953 height -= te.getHeight();
40957 if(this.adjustments){
40958 width += this.adjustments[0];
40959 height += this.adjustments[1];
40961 return {"width": width, "height": height};
40964 setSize : function(width, height){
40965 if(this.fitToFrame && !this.ignoreResize(width, height)){
40966 if(this.fitContainer && this.resizeEl != this.el){
40967 this.el.setSize(width, height);
40969 var size = this.adjustForComponents(width, height);
40971 this.iframeEl.setSize(width,height);
40974 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40975 this.fireEvent('resize', this, size.width, size.height);
40982 * Returns this panel's title
40985 getTitle : function(){
40987 if (typeof(this.title) != 'object') {
40992 for (var k in this.title) {
40993 if (!this.title.hasOwnProperty(k)) {
40997 if (k.indexOf('-') >= 0) {
40998 var s = k.split('-');
40999 for (var i = 0; i<s.length; i++) {
41000 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41003 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41010 * Set this panel's title
41011 * @param {String} title
41013 setTitle : function(title){
41014 this.title = title;
41016 this.region.updatePanelTitle(this, title);
41021 * Returns true is this panel was configured to be closable
41022 * @return {Boolean}
41024 isClosable : function(){
41025 return this.closable;
41028 beforeSlide : function(){
41030 this.resizeEl.clip();
41033 afterSlide : function(){
41035 this.resizeEl.unclip();
41039 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41040 * Will fail silently if the {@link #setUrl} method has not been called.
41041 * This does not activate the panel, just updates its content.
41043 refresh : function(){
41044 if(this.refreshDelegate){
41045 this.loaded = false;
41046 this.refreshDelegate();
41051 * Destroys this panel
41053 destroy : function(){
41054 this.el.removeAllListeners();
41055 var tempEl = document.createElement("span");
41056 tempEl.appendChild(this.el.dom);
41057 tempEl.innerHTML = "";
41063 * form - if the content panel contains a form - this is a reference to it.
41064 * @type {Roo.form.Form}
41068 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41069 * This contains a reference to it.
41075 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41085 * @param {Object} cfg Xtype definition of item to add.
41089 getChildContainer: function () {
41090 return this.getEl();
41094 onScroll : function(e)
41096 this.fireEvent('scroll', this, e);
41101 var ret = new Roo.factory(cfg);
41106 if (cfg.xtype.match(/^Form$/)) {
41109 //if (this.footer) {
41110 // el = this.footer.container.insertSibling(false, 'before');
41112 el = this.el.createChild();
41115 this.form = new Roo.form.Form(cfg);
41118 if ( this.form.allItems.length) {
41119 this.form.render(el.dom);
41123 // should only have one of theses..
41124 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41125 // views.. should not be just added - used named prop 'view''
41127 cfg.el = this.el.appendChild(document.createElement("div"));
41130 var ret = new Roo.factory(cfg);
41132 ret.render && ret.render(false, ''); // render blank..
41142 * @class Roo.bootstrap.panel.Grid
41143 * @extends Roo.bootstrap.panel.Content
41145 * Create a new GridPanel.
41146 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41147 * @param {Object} config A the config object
41153 Roo.bootstrap.panel.Grid = function(config)
41157 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41158 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41160 config.el = this.wrapper;
41161 //this.el = this.wrapper;
41163 if (config.container) {
41164 // ctor'ed from a Border/panel.grid
41167 this.wrapper.setStyle("overflow", "hidden");
41168 this.wrapper.addClass('roo-grid-container');
41173 if(config.toolbar){
41174 var tool_el = this.wrapper.createChild();
41175 this.toolbar = Roo.factory(config.toolbar);
41177 if (config.toolbar.items) {
41178 ti = config.toolbar.items ;
41179 delete config.toolbar.items ;
41183 this.toolbar.render(tool_el);
41184 for(var i =0;i < ti.length;i++) {
41185 // Roo.log(['add child', items[i]]);
41186 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41188 this.toolbar.items = nitems;
41190 delete config.toolbar;
41193 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41194 config.grid.scrollBody = true;;
41195 config.grid.monitorWindowResize = false; // turn off autosizing
41196 config.grid.autoHeight = false;
41197 config.grid.autoWidth = false;
41199 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41201 if (config.background) {
41202 // render grid on panel activation (if panel background)
41203 this.on('activate', function(gp) {
41204 if (!gp.grid.rendered) {
41205 gp.grid.render(this.wrapper);
41206 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41211 this.grid.render(this.wrapper);
41212 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41215 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41216 // ??? needed ??? config.el = this.wrapper;
41221 // xtype created footer. - not sure if will work as we normally have to render first..
41222 if (this.footer && !this.footer.el && this.footer.xtype) {
41224 var ctr = this.grid.getView().getFooterPanel(true);
41225 this.footer.dataSource = this.grid.dataSource;
41226 this.footer = Roo.factory(this.footer, Roo);
41227 this.footer.render(ctr);
41237 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41238 getId : function(){
41239 return this.grid.id;
41243 * Returns the grid for this panel
41244 * @return {Roo.bootstrap.Table}
41246 getGrid : function(){
41250 setSize : function(width, height){
41251 if(!this.ignoreResize(width, height)){
41252 var grid = this.grid;
41253 var size = this.adjustForComponents(width, height);
41254 // tfoot is not a footer?
41257 var gridel = grid.getGridEl();
41258 gridel.setSize(size.width, size.height);
41260 var tbd = grid.getGridEl().select('tbody', true).first();
41261 var thd = grid.getGridEl().select('thead',true).first();
41262 var tbf= grid.getGridEl().select('tfoot', true).first();
41265 size.height -= tbf.getHeight();
41268 size.height -= thd.getHeight();
41271 tbd.setSize(size.width, size.height );
41272 // this is for the account management tab -seems to work there.
41273 var thd = grid.getGridEl().select('thead',true).first();
41275 // tbd.setSize(size.width, size.height - thd.getHeight());
41284 beforeSlide : function(){
41285 this.grid.getView().scroller.clip();
41288 afterSlide : function(){
41289 this.grid.getView().scroller.unclip();
41292 destroy : function(){
41293 this.grid.destroy();
41295 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41300 * @class Roo.bootstrap.panel.Nest
41301 * @extends Roo.bootstrap.panel.Content
41303 * Create a new Panel, that can contain a layout.Border.
41306 * @param {Roo.BorderLayout} layout The layout for this panel
41307 * @param {String/Object} config A string to set only the title or a config object
41309 Roo.bootstrap.panel.Nest = function(config)
41311 // construct with only one argument..
41312 /* FIXME - implement nicer consturctors
41313 if (layout.layout) {
41315 layout = config.layout;
41316 delete config.layout;
41318 if (layout.xtype && !layout.getEl) {
41319 // then layout needs constructing..
41320 layout = Roo.factory(layout, Roo);
41324 config.el = config.layout.getEl();
41326 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41328 config.layout.monitorWindowResize = false; // turn off autosizing
41329 this.layout = config.layout;
41330 this.layout.getEl().addClass("roo-layout-nested-layout");
41331 this.layout.parent = this;
41338 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41340 setSize : function(width, height){
41341 if(!this.ignoreResize(width, height)){
41342 var size = this.adjustForComponents(width, height);
41343 var el = this.layout.getEl();
41344 if (size.height < 1) {
41345 el.setWidth(size.width);
41347 el.setSize(size.width, size.height);
41349 var touch = el.dom.offsetWidth;
41350 this.layout.layout();
41351 // ie requires a double layout on the first pass
41352 if(Roo.isIE && !this.initialized){
41353 this.initialized = true;
41354 this.layout.layout();
41359 // activate all subpanels if not currently active..
41361 setActiveState : function(active){
41362 this.active = active;
41363 this.setActiveClass(active);
41366 this.fireEvent("deactivate", this);
41370 this.fireEvent("activate", this);
41371 // not sure if this should happen before or after..
41372 if (!this.layout) {
41373 return; // should not happen..
41376 for (var r in this.layout.regions) {
41377 reg = this.layout.getRegion(r);
41378 if (reg.getActivePanel()) {
41379 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41380 reg.setActivePanel(reg.getActivePanel());
41383 if (!reg.panels.length) {
41386 reg.showPanel(reg.getPanel(0));
41395 * Returns the nested BorderLayout for this panel
41396 * @return {Roo.BorderLayout}
41398 getLayout : function(){
41399 return this.layout;
41403 * Adds a xtype elements to the layout of the nested panel
41407 xtype : 'ContentPanel',
41414 xtype : 'NestedLayoutPanel',
41420 items : [ ... list of content panels or nested layout panels.. ]
41424 * @param {Object} cfg Xtype definition of item to add.
41426 addxtype : function(cfg) {
41427 return this.layout.addxtype(cfg);
41432 * Ext JS Library 1.1.1
41433 * Copyright(c) 2006-2007, Ext JS, LLC.
41435 * Originally Released Under LGPL - original licence link has changed is not relivant.
41438 * <script type="text/javascript">
41441 * @class Roo.TabPanel
41442 * @extends Roo.util.Observable
41443 * A lightweight tab container.
41447 // basic tabs 1, built from existing content
41448 var tabs = new Roo.TabPanel("tabs1");
41449 tabs.addTab("script", "View Script");
41450 tabs.addTab("markup", "View Markup");
41451 tabs.activate("script");
41453 // more advanced tabs, built from javascript
41454 var jtabs = new Roo.TabPanel("jtabs");
41455 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41457 // set up the UpdateManager
41458 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41459 var updater = tab2.getUpdateManager();
41460 updater.setDefaultUrl("ajax1.htm");
41461 tab2.on('activate', updater.refresh, updater, true);
41463 // Use setUrl for Ajax loading
41464 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41465 tab3.setUrl("ajax2.htm", null, true);
41468 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41471 jtabs.activate("jtabs-1");
41474 * Create a new TabPanel.
41475 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41476 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41478 Roo.bootstrap.panel.Tabs = function(config){
41480 * The container element for this TabPanel.
41481 * @type Roo.Element
41483 this.el = Roo.get(config.el);
41486 if(typeof config == "boolean"){
41487 this.tabPosition = config ? "bottom" : "top";
41489 Roo.apply(this, config);
41493 if(this.tabPosition == "bottom"){
41494 // if tabs are at the bottom = create the body first.
41495 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41496 this.el.addClass("roo-tabs-bottom");
41498 // next create the tabs holders
41500 if (this.tabPosition == "west"){
41502 var reg = this.region; // fake it..
41504 if (!reg.mgr.parent) {
41507 reg = reg.mgr.parent.region;
41509 Roo.log("got nest?");
41511 if (reg.mgr.getRegion('west')) {
41512 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41513 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41514 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41515 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41516 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41524 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41525 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41526 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41527 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41532 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41535 // finally - if tabs are at the top, then create the body last..
41536 if(this.tabPosition != "bottom"){
41537 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41538 * @type Roo.Element
41540 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41541 this.el.addClass("roo-tabs-top");
41545 this.bodyEl.setStyle("position", "relative");
41547 this.active = null;
41548 this.activateDelegate = this.activate.createDelegate(this);
41553 * Fires when the active tab changes
41554 * @param {Roo.TabPanel} this
41555 * @param {Roo.TabPanelItem} activePanel The new active tab
41559 * @event beforetabchange
41560 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41561 * @param {Roo.TabPanel} this
41562 * @param {Object} e Set cancel to true on this object to cancel the tab change
41563 * @param {Roo.TabPanelItem} tab The tab being changed to
41565 "beforetabchange" : true
41568 Roo.EventManager.onWindowResize(this.onResize, this);
41569 this.cpad = this.el.getPadding("lr");
41570 this.hiddenCount = 0;
41573 // toolbar on the tabbar support...
41574 if (this.toolbar) {
41575 alert("no toolbar support yet");
41576 this.toolbar = false;
41578 var tcfg = this.toolbar;
41579 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41580 this.toolbar = new Roo.Toolbar(tcfg);
41581 if (Roo.isSafari) {
41582 var tbl = tcfg.container.child('table', true);
41583 tbl.setAttribute('width', '100%');
41591 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41594 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41596 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41598 tabPosition : "top",
41600 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41602 currentTabWidth : 0,
41604 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41608 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41612 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41614 preferredTabWidth : 175,
41616 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41618 resizeTabs : false,
41620 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41622 monitorResize : true,
41624 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41626 toolbar : false, // set by caller..
41628 region : false, /// set by caller
41630 disableTooltips : true, // not used yet...
41633 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41634 * @param {String} id The id of the div to use <b>or create</b>
41635 * @param {String} text The text for the tab
41636 * @param {String} content (optional) Content to put in the TabPanelItem body
41637 * @param {Boolean} closable (optional) True to create a close icon on the tab
41638 * @return {Roo.TabPanelItem} The created TabPanelItem
41640 addTab : function(id, text, content, closable, tpl)
41642 var item = new Roo.bootstrap.panel.TabItem({
41646 closable : closable,
41649 this.addTabItem(item);
41651 item.setContent(content);
41657 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41658 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41659 * @return {Roo.TabPanelItem}
41661 getTab : function(id){
41662 return this.items[id];
41666 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41667 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41669 hideTab : function(id){
41670 var t = this.items[id];
41673 this.hiddenCount++;
41674 this.autoSizeTabs();
41679 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41680 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41682 unhideTab : function(id){
41683 var t = this.items[id];
41685 t.setHidden(false);
41686 this.hiddenCount--;
41687 this.autoSizeTabs();
41692 * Adds an existing {@link Roo.TabPanelItem}.
41693 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41695 addTabItem : function(item)
41697 this.items[item.id] = item;
41698 this.items.push(item);
41699 this.autoSizeTabs();
41700 // if(this.resizeTabs){
41701 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41702 // this.autoSizeTabs();
41704 // item.autoSize();
41709 * Removes a {@link Roo.TabPanelItem}.
41710 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41712 removeTab : function(id){
41713 var items = this.items;
41714 var tab = items[id];
41715 if(!tab) { return; }
41716 var index = items.indexOf(tab);
41717 if(this.active == tab && items.length > 1){
41718 var newTab = this.getNextAvailable(index);
41723 this.stripEl.dom.removeChild(tab.pnode.dom);
41724 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41725 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41727 items.splice(index, 1);
41728 delete this.items[tab.id];
41729 tab.fireEvent("close", tab);
41730 tab.purgeListeners();
41731 this.autoSizeTabs();
41734 getNextAvailable : function(start){
41735 var items = this.items;
41737 // look for a next tab that will slide over to
41738 // replace the one being removed
41739 while(index < items.length){
41740 var item = items[++index];
41741 if(item && !item.isHidden()){
41745 // if one isn't found select the previous tab (on the left)
41748 var item = items[--index];
41749 if(item && !item.isHidden()){
41757 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41758 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41760 disableTab : function(id){
41761 var tab = this.items[id];
41762 if(tab && this.active != tab){
41768 * Enables a {@link Roo.TabPanelItem} that is disabled.
41769 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41771 enableTab : function(id){
41772 var tab = this.items[id];
41777 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41778 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41779 * @return {Roo.TabPanelItem} The TabPanelItem.
41781 activate : function(id)
41783 //Roo.log('activite:' + id);
41785 var tab = this.items[id];
41789 if(tab == this.active || tab.disabled){
41793 this.fireEvent("beforetabchange", this, e, tab);
41794 if(e.cancel !== true && !tab.disabled){
41796 this.active.hide();
41798 this.active = this.items[id];
41799 this.active.show();
41800 this.fireEvent("tabchange", this, this.active);
41806 * Gets the active {@link Roo.TabPanelItem}.
41807 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41809 getActiveTab : function(){
41810 return this.active;
41814 * Updates the tab body element to fit the height of the container element
41815 * for overflow scrolling
41816 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41818 syncHeight : function(targetHeight){
41819 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41820 var bm = this.bodyEl.getMargins();
41821 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41822 this.bodyEl.setHeight(newHeight);
41826 onResize : function(){
41827 if(this.monitorResize){
41828 this.autoSizeTabs();
41833 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41835 beginUpdate : function(){
41836 this.updating = true;
41840 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41842 endUpdate : function(){
41843 this.updating = false;
41844 this.autoSizeTabs();
41848 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41850 autoSizeTabs : function()
41852 var count = this.items.length;
41853 var vcount = count - this.hiddenCount;
41856 this.stripEl.hide();
41858 this.stripEl.show();
41861 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41866 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41867 var availWidth = Math.floor(w / vcount);
41868 var b = this.stripBody;
41869 if(b.getWidth() > w){
41870 var tabs = this.items;
41871 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41872 if(availWidth < this.minTabWidth){
41873 /*if(!this.sleft){ // incomplete scrolling code
41874 this.createScrollButtons();
41877 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41880 if(this.currentTabWidth < this.preferredTabWidth){
41881 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41887 * Returns the number of tabs in this TabPanel.
41890 getCount : function(){
41891 return this.items.length;
41895 * Resizes all the tabs to the passed width
41896 * @param {Number} The new width
41898 setTabWidth : function(width){
41899 this.currentTabWidth = width;
41900 for(var i = 0, len = this.items.length; i < len; i++) {
41901 if(!this.items[i].isHidden()) {
41902 this.items[i].setWidth(width);
41908 * Destroys this TabPanel
41909 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41911 destroy : function(removeEl){
41912 Roo.EventManager.removeResizeListener(this.onResize, this);
41913 for(var i = 0, len = this.items.length; i < len; i++){
41914 this.items[i].purgeListeners();
41916 if(removeEl === true){
41917 this.el.update("");
41922 createStrip : function(container)
41924 var strip = document.createElement("nav");
41925 strip.className = Roo.bootstrap.version == 4 ?
41926 "navbar-light bg-light" :
41927 "navbar navbar-default"; //"x-tabs-wrap";
41928 container.appendChild(strip);
41932 createStripList : function(strip)
41934 // div wrapper for retard IE
41935 // returns the "tr" element.
41936 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41937 //'<div class="x-tabs-strip-wrap">'+
41938 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41939 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41940 return strip.firstChild; //.firstChild.firstChild.firstChild;
41942 createBody : function(container)
41944 var body = document.createElement("div");
41945 Roo.id(body, "tab-body");
41946 //Roo.fly(body).addClass("x-tabs-body");
41947 Roo.fly(body).addClass("tab-content");
41948 container.appendChild(body);
41951 createItemBody :function(bodyEl, id){
41952 var body = Roo.getDom(id);
41954 body = document.createElement("div");
41957 //Roo.fly(body).addClass("x-tabs-item-body");
41958 Roo.fly(body).addClass("tab-pane");
41959 bodyEl.insertBefore(body, bodyEl.firstChild);
41963 createStripElements : function(stripEl, text, closable, tpl)
41965 var td = document.createElement("li"); // was td..
41966 td.className = 'nav-item';
41968 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41971 stripEl.appendChild(td);
41973 td.className = "x-tabs-closable";
41974 if(!this.closeTpl){
41975 this.closeTpl = new Roo.Template(
41976 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41977 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41978 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41981 var el = this.closeTpl.overwrite(td, {"text": text});
41982 var close = el.getElementsByTagName("div")[0];
41983 var inner = el.getElementsByTagName("em")[0];
41984 return {"el": el, "close": close, "inner": inner};
41987 // not sure what this is..
41988 // if(!this.tabTpl){
41989 //this.tabTpl = new Roo.Template(
41990 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41991 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41993 // this.tabTpl = new Roo.Template(
41994 // '<a href="#">' +
41995 // '<span unselectable="on"' +
41996 // (this.disableTooltips ? '' : ' title="{text}"') +
41997 // ' >{text}</span></a>'
42003 var template = tpl || this.tabTpl || false;
42006 template = new Roo.Template(
42007 Roo.bootstrap.version == 4 ?
42009 '<a class="nav-link" href="#" unselectable="on"' +
42010 (this.disableTooltips ? '' : ' title="{text}"') +
42013 '<a class="nav-link" href="#">' +
42014 '<span unselectable="on"' +
42015 (this.disableTooltips ? '' : ' title="{text}"') +
42016 ' >{text}</span></a>'
42021 switch (typeof(template)) {
42025 template = new Roo.Template(template);
42031 var el = template.overwrite(td, {"text": text});
42033 var inner = el.getElementsByTagName("span")[0];
42035 return {"el": el, "inner": inner};
42043 * @class Roo.TabPanelItem
42044 * @extends Roo.util.Observable
42045 * Represents an individual item (tab plus body) in a TabPanel.
42046 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42047 * @param {String} id The id of this TabPanelItem
42048 * @param {String} text The text for the tab of this TabPanelItem
42049 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42051 Roo.bootstrap.panel.TabItem = function(config){
42053 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42054 * @type Roo.TabPanel
42056 this.tabPanel = config.panel;
42058 * The id for this TabPanelItem
42061 this.id = config.id;
42063 this.disabled = false;
42065 this.text = config.text;
42067 this.loaded = false;
42068 this.closable = config.closable;
42071 * The body element for this TabPanelItem.
42072 * @type Roo.Element
42074 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42075 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42076 this.bodyEl.setStyle("display", "block");
42077 this.bodyEl.setStyle("zoom", "1");
42078 //this.hideAction();
42080 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42082 this.el = Roo.get(els.el);
42083 this.inner = Roo.get(els.inner, true);
42084 this.textEl = Roo.bootstrap.version == 4 ?
42085 this.el : Roo.get(this.el.dom.firstChild, true);
42087 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42088 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42091 // this.el.on("mousedown", this.onTabMouseDown, this);
42092 this.el.on("click", this.onTabClick, this);
42094 if(config.closable){
42095 var c = Roo.get(els.close, true);
42096 c.dom.title = this.closeText;
42097 c.addClassOnOver("close-over");
42098 c.on("click", this.closeClick, this);
42104 * Fires when this tab becomes the active tab.
42105 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42106 * @param {Roo.TabPanelItem} this
42110 * @event beforeclose
42111 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42112 * @param {Roo.TabPanelItem} this
42113 * @param {Object} e Set cancel to true on this object to cancel the close.
42115 "beforeclose": true,
42118 * Fires when this tab is closed.
42119 * @param {Roo.TabPanelItem} this
42123 * @event deactivate
42124 * Fires when this tab is no longer the active tab.
42125 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42126 * @param {Roo.TabPanelItem} this
42128 "deactivate" : true
42130 this.hidden = false;
42132 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42135 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42137 purgeListeners : function(){
42138 Roo.util.Observable.prototype.purgeListeners.call(this);
42139 this.el.removeAllListeners();
42142 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42145 this.status_node.addClass("active");
42148 this.tabPanel.stripWrap.repaint();
42150 this.fireEvent("activate", this.tabPanel, this);
42154 * Returns true if this tab is the active tab.
42155 * @return {Boolean}
42157 isActive : function(){
42158 return this.tabPanel.getActiveTab() == this;
42162 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42165 this.status_node.removeClass("active");
42167 this.fireEvent("deactivate", this.tabPanel, this);
42170 hideAction : function(){
42171 this.bodyEl.hide();
42172 this.bodyEl.setStyle("position", "absolute");
42173 this.bodyEl.setLeft("-20000px");
42174 this.bodyEl.setTop("-20000px");
42177 showAction : function(){
42178 this.bodyEl.setStyle("position", "relative");
42179 this.bodyEl.setTop("");
42180 this.bodyEl.setLeft("");
42181 this.bodyEl.show();
42185 * Set the tooltip for the tab.
42186 * @param {String} tooltip The tab's tooltip
42188 setTooltip : function(text){
42189 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42190 this.textEl.dom.qtip = text;
42191 this.textEl.dom.removeAttribute('title');
42193 this.textEl.dom.title = text;
42197 onTabClick : function(e){
42198 e.preventDefault();
42199 this.tabPanel.activate(this.id);
42202 onTabMouseDown : function(e){
42203 e.preventDefault();
42204 this.tabPanel.activate(this.id);
42207 getWidth : function(){
42208 return this.inner.getWidth();
42211 setWidth : function(width){
42212 var iwidth = width - this.linode.getPadding("lr");
42213 this.inner.setWidth(iwidth);
42214 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42215 this.linode.setWidth(width);
42219 * Show or hide the tab
42220 * @param {Boolean} hidden True to hide or false to show.
42222 setHidden : function(hidden){
42223 this.hidden = hidden;
42224 this.linode.setStyle("display", hidden ? "none" : "");
42228 * Returns true if this tab is "hidden"
42229 * @return {Boolean}
42231 isHidden : function(){
42232 return this.hidden;
42236 * Returns the text for this tab
42239 getText : function(){
42243 autoSize : function(){
42244 //this.el.beginMeasure();
42245 this.textEl.setWidth(1);
42247 * #2804 [new] Tabs in Roojs
42248 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42250 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42251 //this.el.endMeasure();
42255 * Sets the text for the tab (Note: this also sets the tooltip text)
42256 * @param {String} text The tab's text and tooltip
42258 setText : function(text){
42260 this.textEl.update(text);
42261 this.setTooltip(text);
42262 //if(!this.tabPanel.resizeTabs){
42263 // this.autoSize();
42267 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42269 activate : function(){
42270 this.tabPanel.activate(this.id);
42274 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42276 disable : function(){
42277 if(this.tabPanel.active != this){
42278 this.disabled = true;
42279 this.status_node.addClass("disabled");
42284 * Enables this TabPanelItem if it was previously disabled.
42286 enable : function(){
42287 this.disabled = false;
42288 this.status_node.removeClass("disabled");
42292 * Sets the content for this TabPanelItem.
42293 * @param {String} content The content
42294 * @param {Boolean} loadScripts true to look for and load scripts
42296 setContent : function(content, loadScripts){
42297 this.bodyEl.update(content, loadScripts);
42301 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42302 * @return {Roo.UpdateManager} The UpdateManager
42304 getUpdateManager : function(){
42305 return this.bodyEl.getUpdateManager();
42309 * Set a URL to be used to load the content for this TabPanelItem.
42310 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42311 * @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)
42312 * @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)
42313 * @return {Roo.UpdateManager} The UpdateManager
42315 setUrl : function(url, params, loadOnce){
42316 if(this.refreshDelegate){
42317 this.un('activate', this.refreshDelegate);
42319 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42320 this.on("activate", this.refreshDelegate);
42321 return this.bodyEl.getUpdateManager();
42325 _handleRefresh : function(url, params, loadOnce){
42326 if(!loadOnce || !this.loaded){
42327 var updater = this.bodyEl.getUpdateManager();
42328 updater.update(url, params, this._setLoaded.createDelegate(this));
42333 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42334 * Will fail silently if the setUrl method has not been called.
42335 * This does not activate the panel, just updates its content.
42337 refresh : function(){
42338 if(this.refreshDelegate){
42339 this.loaded = false;
42340 this.refreshDelegate();
42345 _setLoaded : function(){
42346 this.loaded = true;
42350 closeClick : function(e){
42353 this.fireEvent("beforeclose", this, o);
42354 if(o.cancel !== true){
42355 this.tabPanel.removeTab(this.id);
42359 * The text displayed in the tooltip for the close icon.
42362 closeText : "Close this tab"
42365 * This script refer to:
42366 * Title: International Telephone Input
42367 * Author: Jack O'Connor
42368 * Code version: v12.1.12
42369 * Availability: https://github.com/jackocnr/intl-tel-input.git
42372 Roo.bootstrap.PhoneInputData = function() {
42375 "Afghanistan (افغانستان)",
42380 "Albania (Shqipëri)",
42385 "Algeria (الجزائر)",
42410 "Antigua and Barbuda",
42420 "Armenia (Հայաստան)",
42436 "Austria (Österreich)",
42441 "Azerbaijan (Azərbaycan)",
42451 "Bahrain (البحرين)",
42456 "Bangladesh (বাংলাদেশ)",
42466 "Belarus (Беларусь)",
42471 "Belgium (België)",
42501 "Bosnia and Herzegovina (Босна и Херцеговина)",
42516 "British Indian Ocean Territory",
42521 "British Virgin Islands",
42531 "Bulgaria (България)",
42541 "Burundi (Uburundi)",
42546 "Cambodia (កម្ពុជា)",
42551 "Cameroon (Cameroun)",
42560 ["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"]
42563 "Cape Verde (Kabu Verdi)",
42568 "Caribbean Netherlands",
42579 "Central African Republic (République centrafricaine)",
42599 "Christmas Island",
42605 "Cocos (Keeling) Islands",
42616 "Comoros (جزر القمر)",
42621 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42626 "Congo (Republic) (Congo-Brazzaville)",
42646 "Croatia (Hrvatska)",
42667 "Czech Republic (Česká republika)",
42672 "Denmark (Danmark)",
42687 "Dominican Republic (República Dominicana)",
42691 ["809", "829", "849"]
42709 "Equatorial Guinea (Guinea Ecuatorial)",
42729 "Falkland Islands (Islas Malvinas)",
42734 "Faroe Islands (Føroyar)",
42755 "French Guiana (Guyane française)",
42760 "French Polynesia (Polynésie française)",
42775 "Georgia (საქართველო)",
42780 "Germany (Deutschland)",
42800 "Greenland (Kalaallit Nunaat)",
42837 "Guinea-Bissau (Guiné Bissau)",
42862 "Hungary (Magyarország)",
42867 "Iceland (Ísland)",
42887 "Iraq (العراق)",
42903 "Israel (ישראל)",
42930 "Jordan (الأردن)",
42935 "Kazakhstan (Казахстан)",
42956 "Kuwait (الكويت)",
42961 "Kyrgyzstan (Кыргызстан)",
42971 "Latvia (Latvija)",
42976 "Lebanon (لبنان)",
42991 "Libya (ليبيا)",
43001 "Lithuania (Lietuva)",
43016 "Macedonia (FYROM) (Македонија)",
43021 "Madagascar (Madagasikara)",
43051 "Marshall Islands",
43061 "Mauritania (موريتانيا)",
43066 "Mauritius (Moris)",
43087 "Moldova (Republica Moldova)",
43097 "Mongolia (Монгол)",
43102 "Montenegro (Crna Gora)",
43112 "Morocco (المغرب)",
43118 "Mozambique (Moçambique)",
43123 "Myanmar (Burma) (မြန်မာ)",
43128 "Namibia (Namibië)",
43143 "Netherlands (Nederland)",
43148 "New Caledonia (Nouvelle-Calédonie)",
43183 "North Korea (조선 민주주의 인민 공화국)",
43188 "Northern Mariana Islands",
43204 "Pakistan (پاکستان)",
43214 "Palestine (فلسطين)",
43224 "Papua New Guinea",
43266 "Réunion (La Réunion)",
43272 "Romania (România)",
43288 "Saint Barthélemy",
43299 "Saint Kitts and Nevis",
43309 "Saint Martin (Saint-Martin (partie française))",
43315 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43320 "Saint Vincent and the Grenadines",
43335 "São Tomé and Príncipe (São Tomé e Príncipe)",
43340 "Saudi Arabia (المملكة العربية السعودية)",
43345 "Senegal (Sénégal)",
43375 "Slovakia (Slovensko)",
43380 "Slovenia (Slovenija)",
43390 "Somalia (Soomaaliya)",
43400 "South Korea (대한민국)",
43405 "South Sudan (جنوب السودان)",
43415 "Sri Lanka (ශ්රී ලංකාව)",
43420 "Sudan (السودان)",
43430 "Svalbard and Jan Mayen",
43441 "Sweden (Sverige)",
43446 "Switzerland (Schweiz)",
43451 "Syria (سوريا)",
43496 "Trinidad and Tobago",
43501 "Tunisia (تونس)",
43506 "Turkey (Türkiye)",
43516 "Turks and Caicos Islands",
43526 "U.S. Virgin Islands",
43536 "Ukraine (Україна)",
43541 "United Arab Emirates (الإمارات العربية المتحدة)",
43563 "Uzbekistan (Oʻzbekiston)",
43573 "Vatican City (Città del Vaticano)",
43584 "Vietnam (Việt Nam)",
43589 "Wallis and Futuna (Wallis-et-Futuna)",
43594 "Western Sahara (الصحراء الغربية)",
43600 "Yemen (اليمن)",
43624 * This script refer to:
43625 * Title: International Telephone Input
43626 * Author: Jack O'Connor
43627 * Code version: v12.1.12
43628 * Availability: https://github.com/jackocnr/intl-tel-input.git
43632 * @class Roo.bootstrap.PhoneInput
43633 * @extends Roo.bootstrap.TriggerField
43634 * An input with International dial-code selection
43636 * @cfg {String} defaultDialCode default '+852'
43637 * @cfg {Array} preferedCountries default []
43640 * Create a new PhoneInput.
43641 * @param {Object} config Configuration options
43644 Roo.bootstrap.PhoneInput = function(config) {
43645 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43648 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43650 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43652 listWidth: undefined,
43654 selectedClass: 'active',
43656 invalidClass : "has-warning",
43658 validClass: 'has-success',
43660 allowed: '0123456789',
43665 * @cfg {String} defaultDialCode The default dial code when initializing the input
43667 defaultDialCode: '+852',
43670 * @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
43672 preferedCountries: false,
43674 getAutoCreate : function()
43676 var data = Roo.bootstrap.PhoneInputData();
43677 var align = this.labelAlign || this.parentLabelAlign();
43680 this.allCountries = [];
43681 this.dialCodeMapping = [];
43683 for (var i = 0; i < data.length; i++) {
43685 this.allCountries[i] = {
43689 priority: c[3] || 0,
43690 areaCodes: c[4] || null
43692 this.dialCodeMapping[c[2]] = {
43695 priority: c[3] || 0,
43696 areaCodes: c[4] || null
43708 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43709 maxlength: this.max_length,
43710 cls : 'form-control tel-input',
43711 autocomplete: 'new-password'
43714 var hiddenInput = {
43717 cls: 'hidden-tel-input'
43721 hiddenInput.name = this.name;
43724 if (this.disabled) {
43725 input.disabled = true;
43728 var flag_container = {
43745 cls: this.hasFeedback ? 'has-feedback' : '',
43751 cls: 'dial-code-holder',
43758 cls: 'roo-select2-container input-group',
43765 if (this.fieldLabel.length) {
43768 tooltip: 'This field is required'
43774 cls: 'control-label',
43780 html: this.fieldLabel
43783 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43789 if(this.indicatorpos == 'right') {
43790 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43797 if(align == 'left') {
43805 if(this.labelWidth > 12){
43806 label.style = "width: " + this.labelWidth + 'px';
43808 if(this.labelWidth < 13 && this.labelmd == 0){
43809 this.labelmd = this.labelWidth;
43811 if(this.labellg > 0){
43812 label.cls += ' col-lg-' + this.labellg;
43813 input.cls += ' col-lg-' + (12 - this.labellg);
43815 if(this.labelmd > 0){
43816 label.cls += ' col-md-' + this.labelmd;
43817 container.cls += ' col-md-' + (12 - this.labelmd);
43819 if(this.labelsm > 0){
43820 label.cls += ' col-sm-' + this.labelsm;
43821 container.cls += ' col-sm-' + (12 - this.labelsm);
43823 if(this.labelxs > 0){
43824 label.cls += ' col-xs-' + this.labelxs;
43825 container.cls += ' col-xs-' + (12 - this.labelxs);
43835 var settings = this;
43837 ['xs','sm','md','lg'].map(function(size){
43838 if (settings[size]) {
43839 cfg.cls += ' col-' + size + '-' + settings[size];
43843 this.store = new Roo.data.Store({
43844 proxy : new Roo.data.MemoryProxy({}),
43845 reader : new Roo.data.JsonReader({
43856 'name' : 'dialCode',
43860 'name' : 'priority',
43864 'name' : 'areaCodes',
43871 if(!this.preferedCountries) {
43872 this.preferedCountries = [
43879 var p = this.preferedCountries.reverse();
43882 for (var i = 0; i < p.length; i++) {
43883 for (var j = 0; j < this.allCountries.length; j++) {
43884 if(this.allCountries[j].iso2 == p[i]) {
43885 var t = this.allCountries[j];
43886 this.allCountries.splice(j,1);
43887 this.allCountries.unshift(t);
43893 this.store.proxy.data = {
43895 data: this.allCountries
43901 initEvents : function()
43904 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43906 this.indicator = this.indicatorEl();
43907 this.flag = this.flagEl();
43908 this.dialCodeHolder = this.dialCodeHolderEl();
43910 this.trigger = this.el.select('div.flag-box',true).first();
43911 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43916 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43917 _this.list.setWidth(lw);
43920 this.list.on('mouseover', this.onViewOver, this);
43921 this.list.on('mousemove', this.onViewMove, this);
43922 this.inputEl().on("keyup", this.onKeyUp, this);
43923 this.inputEl().on("keypress", this.onKeyPress, this);
43925 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43927 this.view = new Roo.View(this.list, this.tpl, {
43928 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43931 this.view.on('click', this.onViewClick, this);
43932 this.setValue(this.defaultDialCode);
43935 onTriggerClick : function(e)
43937 Roo.log('trigger click');
43942 if(this.isExpanded()){
43944 this.hasFocus = false;
43946 this.store.load({});
43947 this.hasFocus = true;
43952 isExpanded : function()
43954 return this.list.isVisible();
43957 collapse : function()
43959 if(!this.isExpanded()){
43963 Roo.get(document).un('mousedown', this.collapseIf, this);
43964 Roo.get(document).un('mousewheel', this.collapseIf, this);
43965 this.fireEvent('collapse', this);
43969 expand : function()
43973 if(this.isExpanded() || !this.hasFocus){
43977 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43978 this.list.setWidth(lw);
43981 this.restrictHeight();
43983 Roo.get(document).on('mousedown', this.collapseIf, this);
43984 Roo.get(document).on('mousewheel', this.collapseIf, this);
43986 this.fireEvent('expand', this);
43989 restrictHeight : function()
43991 this.list.alignTo(this.inputEl(), this.listAlign);
43992 this.list.alignTo(this.inputEl(), this.listAlign);
43995 onViewOver : function(e, t)
43997 if(this.inKeyMode){
44000 var item = this.view.findItemFromChild(t);
44003 var index = this.view.indexOf(item);
44004 this.select(index, false);
44009 onViewClick : function(view, doFocus, el, e)
44011 var index = this.view.getSelectedIndexes()[0];
44013 var r = this.store.getAt(index);
44016 this.onSelect(r, index);
44018 if(doFocus !== false && !this.blockFocus){
44019 this.inputEl().focus();
44023 onViewMove : function(e, t)
44025 this.inKeyMode = false;
44028 select : function(index, scrollIntoView)
44030 this.selectedIndex = index;
44031 this.view.select(index);
44032 if(scrollIntoView !== false){
44033 var el = this.view.getNode(index);
44035 this.list.scrollChildIntoView(el, false);
44040 createList : function()
44042 this.list = Roo.get(document.body).createChild({
44044 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44045 style: 'display:none'
44048 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44051 collapseIf : function(e)
44053 var in_combo = e.within(this.el);
44054 var in_list = e.within(this.list);
44055 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44057 if (in_combo || in_list || is_list) {
44063 onSelect : function(record, index)
44065 if(this.fireEvent('beforeselect', this, record, index) !== false){
44067 this.setFlagClass(record.data.iso2);
44068 this.setDialCode(record.data.dialCode);
44069 this.hasFocus = false;
44071 this.fireEvent('select', this, record, index);
44075 flagEl : function()
44077 var flag = this.el.select('div.flag',true).first();
44084 dialCodeHolderEl : function()
44086 var d = this.el.select('input.dial-code-holder',true).first();
44093 setDialCode : function(v)
44095 this.dialCodeHolder.dom.value = '+'+v;
44098 setFlagClass : function(n)
44100 this.flag.dom.className = 'flag '+n;
44103 getValue : function()
44105 var v = this.inputEl().getValue();
44106 if(this.dialCodeHolder) {
44107 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44112 setValue : function(v)
44114 var d = this.getDialCode(v);
44116 //invalid dial code
44117 if(v.length == 0 || !d || d.length == 0) {
44119 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44120 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44126 this.setFlagClass(this.dialCodeMapping[d].iso2);
44127 this.setDialCode(d);
44128 this.inputEl().dom.value = v.replace('+'+d,'');
44129 this.hiddenEl().dom.value = this.getValue();
44134 getDialCode : function(v)
44138 if (v.length == 0) {
44139 return this.dialCodeHolder.dom.value;
44143 if (v.charAt(0) != "+") {
44146 var numericChars = "";
44147 for (var i = 1; i < v.length; i++) {
44148 var c = v.charAt(i);
44151 if (this.dialCodeMapping[numericChars]) {
44152 dialCode = v.substr(1, i);
44154 if (numericChars.length == 4) {
44164 this.setValue(this.defaultDialCode);
44168 hiddenEl : function()
44170 return this.el.select('input.hidden-tel-input',true).first();
44173 // after setting val
44174 onKeyUp : function(e){
44175 this.setValue(this.getValue());
44178 onKeyPress : function(e){
44179 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44186 * @class Roo.bootstrap.MoneyField
44187 * @extends Roo.bootstrap.ComboBox
44188 * Bootstrap MoneyField class
44191 * Create a new MoneyField.
44192 * @param {Object} config Configuration options
44195 Roo.bootstrap.MoneyField = function(config) {
44197 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44201 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44204 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44206 allowDecimals : true,
44208 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44210 decimalSeparator : ".",
44212 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44214 decimalPrecision : 0,
44216 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44218 allowNegative : true,
44220 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44224 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44226 minValue : Number.NEGATIVE_INFINITY,
44228 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44230 maxValue : Number.MAX_VALUE,
44232 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44234 minText : "The minimum value for this field is {0}",
44236 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44238 maxText : "The maximum value for this field is {0}",
44240 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44241 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44243 nanText : "{0} is not a valid number",
44245 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44249 * @cfg {String} defaults currency of the MoneyField
44250 * value should be in lkey
44252 defaultCurrency : false,
44254 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44256 thousandsDelimiter : false,
44258 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44269 getAutoCreate : function()
44271 var align = this.labelAlign || this.parentLabelAlign();
44283 cls : 'form-control roo-money-amount-input',
44284 autocomplete: 'new-password'
44287 var hiddenInput = {
44291 cls: 'hidden-number-input'
44294 if(this.max_length) {
44295 input.maxlength = this.max_length;
44299 hiddenInput.name = this.name;
44302 if (this.disabled) {
44303 input.disabled = true;
44306 var clg = 12 - this.inputlg;
44307 var cmd = 12 - this.inputmd;
44308 var csm = 12 - this.inputsm;
44309 var cxs = 12 - this.inputxs;
44313 cls : 'row roo-money-field',
44317 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44321 cls: 'roo-select2-container input-group',
44325 cls : 'form-control roo-money-currency-input',
44326 autocomplete: 'new-password',
44328 name : this.currencyName
44332 cls : 'input-group-addon',
44346 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44350 cls: this.hasFeedback ? 'has-feedback' : '',
44361 if (this.fieldLabel.length) {
44364 tooltip: 'This field is required'
44370 cls: 'control-label',
44376 html: this.fieldLabel
44379 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44385 if(this.indicatorpos == 'right') {
44386 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44393 if(align == 'left') {
44401 if(this.labelWidth > 12){
44402 label.style = "width: " + this.labelWidth + 'px';
44404 if(this.labelWidth < 13 && this.labelmd == 0){
44405 this.labelmd = this.labelWidth;
44407 if(this.labellg > 0){
44408 label.cls += ' col-lg-' + this.labellg;
44409 input.cls += ' col-lg-' + (12 - this.labellg);
44411 if(this.labelmd > 0){
44412 label.cls += ' col-md-' + this.labelmd;
44413 container.cls += ' col-md-' + (12 - this.labelmd);
44415 if(this.labelsm > 0){
44416 label.cls += ' col-sm-' + this.labelsm;
44417 container.cls += ' col-sm-' + (12 - this.labelsm);
44419 if(this.labelxs > 0){
44420 label.cls += ' col-xs-' + this.labelxs;
44421 container.cls += ' col-xs-' + (12 - this.labelxs);
44432 var settings = this;
44434 ['xs','sm','md','lg'].map(function(size){
44435 if (settings[size]) {
44436 cfg.cls += ' col-' + size + '-' + settings[size];
44443 initEvents : function()
44445 this.indicator = this.indicatorEl();
44447 this.initCurrencyEvent();
44449 this.initNumberEvent();
44452 initCurrencyEvent : function()
44455 throw "can not find store for combo";
44458 this.store = Roo.factory(this.store, Roo.data);
44459 this.store.parent = this;
44463 this.triggerEl = this.el.select('.input-group-addon', true).first();
44465 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44470 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44471 _this.list.setWidth(lw);
44474 this.list.on('mouseover', this.onViewOver, this);
44475 this.list.on('mousemove', this.onViewMove, this);
44476 this.list.on('scroll', this.onViewScroll, this);
44479 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44482 this.view = new Roo.View(this.list, this.tpl, {
44483 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44486 this.view.on('click', this.onViewClick, this);
44488 this.store.on('beforeload', this.onBeforeLoad, this);
44489 this.store.on('load', this.onLoad, this);
44490 this.store.on('loadexception', this.onLoadException, this);
44492 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44493 "up" : function(e){
44494 this.inKeyMode = true;
44498 "down" : function(e){
44499 if(!this.isExpanded()){
44500 this.onTriggerClick();
44502 this.inKeyMode = true;
44507 "enter" : function(e){
44510 if(this.fireEvent("specialkey", this, e)){
44511 this.onViewClick(false);
44517 "esc" : function(e){
44521 "tab" : function(e){
44524 if(this.fireEvent("specialkey", this, e)){
44525 this.onViewClick(false);
44533 doRelay : function(foo, bar, hname){
44534 if(hname == 'down' || this.scope.isExpanded()){
44535 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44543 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44547 initNumberEvent : function(e)
44549 this.inputEl().on("keydown" , this.fireKey, this);
44550 this.inputEl().on("focus", this.onFocus, this);
44551 this.inputEl().on("blur", this.onBlur, this);
44553 this.inputEl().relayEvent('keyup', this);
44555 if(this.indicator){
44556 this.indicator.addClass('invisible');
44559 this.originalValue = this.getValue();
44561 if(this.validationEvent == 'keyup'){
44562 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44563 this.inputEl().on('keyup', this.filterValidation, this);
44565 else if(this.validationEvent !== false){
44566 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44569 if(this.selectOnFocus){
44570 this.on("focus", this.preFocus, this);
44573 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44574 this.inputEl().on("keypress", this.filterKeys, this);
44576 this.inputEl().relayEvent('keypress', this);
44579 var allowed = "0123456789";
44581 if(this.allowDecimals){
44582 allowed += this.decimalSeparator;
44585 if(this.allowNegative){
44589 if(this.thousandsDelimiter) {
44593 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44595 var keyPress = function(e){
44597 var k = e.getKey();
44599 var c = e.getCharCode();
44602 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44603 allowed.indexOf(String.fromCharCode(c)) === -1
44609 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44613 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44618 this.inputEl().on("keypress", keyPress, this);
44622 onTriggerClick : function(e)
44629 this.loadNext = false;
44631 if(this.isExpanded()){
44636 this.hasFocus = true;
44638 if(this.triggerAction == 'all') {
44639 this.doQuery(this.allQuery, true);
44643 this.doQuery(this.getRawValue());
44646 getCurrency : function()
44648 var v = this.currencyEl().getValue();
44653 restrictHeight : function()
44655 this.list.alignTo(this.currencyEl(), this.listAlign);
44656 this.list.alignTo(this.currencyEl(), this.listAlign);
44659 onViewClick : function(view, doFocus, el, e)
44661 var index = this.view.getSelectedIndexes()[0];
44663 var r = this.store.getAt(index);
44666 this.onSelect(r, index);
44670 onSelect : function(record, index){
44672 if(this.fireEvent('beforeselect', this, record, index) !== false){
44674 this.setFromCurrencyData(index > -1 ? record.data : false);
44678 this.fireEvent('select', this, record, index);
44682 setFromCurrencyData : function(o)
44686 this.lastCurrency = o;
44688 if (this.currencyField) {
44689 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44691 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44694 this.lastSelectionText = currency;
44696 //setting default currency
44697 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44698 this.setCurrency(this.defaultCurrency);
44702 this.setCurrency(currency);
44705 setFromData : function(o)
44709 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44711 this.setFromCurrencyData(c);
44716 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44718 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44721 this.setValue(value);
44725 setCurrency : function(v)
44727 this.currencyValue = v;
44730 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44735 setValue : function(v)
44737 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44743 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44745 this.inputEl().dom.value = (v == '') ? '' :
44746 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44748 if(!this.allowZero && v === '0') {
44749 this.hiddenEl().dom.value = '';
44750 this.inputEl().dom.value = '';
44757 getRawValue : function()
44759 var v = this.inputEl().getValue();
44764 getValue : function()
44766 return this.fixPrecision(this.parseValue(this.getRawValue()));
44769 parseValue : function(value)
44771 if(this.thousandsDelimiter) {
44773 r = new RegExp(",", "g");
44774 value = value.replace(r, "");
44777 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44778 return isNaN(value) ? '' : value;
44782 fixPrecision : function(value)
44784 if(this.thousandsDelimiter) {
44786 r = new RegExp(",", "g");
44787 value = value.replace(r, "");
44790 var nan = isNaN(value);
44792 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44793 return nan ? '' : value;
44795 return parseFloat(value).toFixed(this.decimalPrecision);
44798 decimalPrecisionFcn : function(v)
44800 return Math.floor(v);
44803 validateValue : function(value)
44805 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44809 var num = this.parseValue(value);
44812 this.markInvalid(String.format(this.nanText, value));
44816 if(num < this.minValue){
44817 this.markInvalid(String.format(this.minText, this.minValue));
44821 if(num > this.maxValue){
44822 this.markInvalid(String.format(this.maxText, this.maxValue));
44829 validate : function()
44831 if(this.disabled || this.allowBlank){
44836 var currency = this.getCurrency();
44838 if(this.validateValue(this.getRawValue()) && currency.length){
44843 this.markInvalid();
44847 getName: function()
44852 beforeBlur : function()
44858 var v = this.parseValue(this.getRawValue());
44865 onBlur : function()
44869 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44870 //this.el.removeClass(this.focusClass);
44873 this.hasFocus = false;
44875 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44879 var v = this.getValue();
44881 if(String(v) !== String(this.startValue)){
44882 this.fireEvent('change', this, v, this.startValue);
44885 this.fireEvent("blur", this);
44888 inputEl : function()
44890 return this.el.select('.roo-money-amount-input', true).first();
44893 currencyEl : function()
44895 return this.el.select('.roo-money-currency-input', true).first();
44898 hiddenEl : function()
44900 return this.el.select('input.hidden-number-input',true).first();
44904 * @class Roo.bootstrap.BezierSignature
44905 * @extends Roo.bootstrap.Component
44906 * Bootstrap BezierSignature class
44907 * This script refer to:
44908 * Title: Signature Pad
44910 * Availability: https://github.com/szimek/signature_pad
44913 * Create a new BezierSignature
44914 * @param {Object} config The config object
44917 Roo.bootstrap.BezierSignature = function(config){
44918 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44924 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44931 mouse_btn_down: true,
44934 * @cfg {int} canvas height
44936 canvas_height: '200px',
44939 * @cfg {float|function} Radius of a single dot.
44944 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44949 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44954 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44959 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44964 * @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.
44966 bg_color: 'rgba(0, 0, 0, 0)',
44969 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44971 dot_color: 'black',
44974 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44976 velocity_filter_weight: 0.7,
44979 * @cfg {function} Callback when stroke begin.
44984 * @cfg {function} Callback when stroke end.
44988 getAutoCreate : function()
44990 var cls = 'roo-signature column';
44993 cls += ' ' + this.cls;
45003 for(var i = 0; i < col_sizes.length; i++) {
45004 if(this[col_sizes[i]]) {
45005 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45015 cls: 'roo-signature-body',
45019 cls: 'roo-signature-body-canvas',
45020 height: this.canvas_height,
45021 width: this.canvas_width
45028 style: 'display: none'
45036 initEvents: function()
45038 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45040 var canvas = this.canvasEl();
45042 // mouse && touch event swapping...
45043 canvas.dom.style.touchAction = 'none';
45044 canvas.dom.style.msTouchAction = 'none';
45046 this.mouse_btn_down = false;
45047 canvas.on('mousedown', this._handleMouseDown, this);
45048 canvas.on('mousemove', this._handleMouseMove, this);
45049 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45051 if (window.PointerEvent) {
45052 canvas.on('pointerdown', this._handleMouseDown, this);
45053 canvas.on('pointermove', this._handleMouseMove, this);
45054 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45057 if ('ontouchstart' in window) {
45058 canvas.on('touchstart', this._handleTouchStart, this);
45059 canvas.on('touchmove', this._handleTouchMove, this);
45060 canvas.on('touchend', this._handleTouchEnd, this);
45063 Roo.EventManager.onWindowResize(this.resize, this, true);
45065 // file input event
45066 this.fileEl().on('change', this.uploadImage, this);
45073 resize: function(){
45075 var canvas = this.canvasEl().dom;
45076 var ctx = this.canvasElCtx();
45077 var img_data = false;
45079 if(canvas.width > 0) {
45080 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45082 // setting canvas width will clean img data
45085 var style = window.getComputedStyle ?
45086 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45088 var padding_left = parseInt(style.paddingLeft) || 0;
45089 var padding_right = parseInt(style.paddingRight) || 0;
45091 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45094 ctx.putImageData(img_data, 0, 0);
45098 _handleMouseDown: function(e)
45100 if (e.browserEvent.which === 1) {
45101 this.mouse_btn_down = true;
45102 this.strokeBegin(e);
45106 _handleMouseMove: function (e)
45108 if (this.mouse_btn_down) {
45109 this.strokeMoveUpdate(e);
45113 _handleMouseUp: function (e)
45115 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45116 this.mouse_btn_down = false;
45121 _handleTouchStart: function (e) {
45123 e.preventDefault();
45124 if (e.browserEvent.targetTouches.length === 1) {
45125 // var touch = e.browserEvent.changedTouches[0];
45126 // this.strokeBegin(touch);
45128 this.strokeBegin(e); // assume e catching the correct xy...
45132 _handleTouchMove: function (e) {
45133 e.preventDefault();
45134 // var touch = event.targetTouches[0];
45135 // _this._strokeMoveUpdate(touch);
45136 this.strokeMoveUpdate(e);
45139 _handleTouchEnd: function (e) {
45140 var wasCanvasTouched = e.target === this.canvasEl().dom;
45141 if (wasCanvasTouched) {
45142 e.preventDefault();
45143 // var touch = event.changedTouches[0];
45144 // _this._strokeEnd(touch);
45149 reset: function () {
45150 this._lastPoints = [];
45151 this._lastVelocity = 0;
45152 this._lastWidth = (this.min_width + this.max_width) / 2;
45153 this.canvasElCtx().fillStyle = this.dot_color;
45156 strokeMoveUpdate: function(e)
45158 this.strokeUpdate(e);
45160 if (this.throttle) {
45161 this.throttleStroke(this.strokeUpdate, this.throttle);
45164 this.strokeUpdate(e);
45168 strokeBegin: function(e)
45170 var newPointGroup = {
45171 color: this.dot_color,
45175 if (typeof this.onBegin === 'function') {
45179 this.curve_data.push(newPointGroup);
45181 this.strokeUpdate(e);
45184 strokeUpdate: function(e)
45186 var rect = this.canvasEl().dom.getBoundingClientRect();
45187 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45188 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45189 var lastPoints = lastPointGroup.points;
45190 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45191 var isLastPointTooClose = lastPoint
45192 ? point.distanceTo(lastPoint) <= this.min_distance
45194 var color = lastPointGroup.color;
45195 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45196 var curve = this.addPoint(point);
45198 this.drawDot({color: color, point: point});
45201 this.drawCurve({color: color, curve: curve});
45211 strokeEnd: function(e)
45213 this.strokeUpdate(e);
45214 if (typeof this.onEnd === 'function') {
45219 addPoint: function (point) {
45220 var _lastPoints = this._lastPoints;
45221 _lastPoints.push(point);
45222 if (_lastPoints.length > 2) {
45223 if (_lastPoints.length === 3) {
45224 _lastPoints.unshift(_lastPoints[0]);
45226 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45227 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45228 _lastPoints.shift();
45234 calculateCurveWidths: function (startPoint, endPoint) {
45235 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45236 (1 - this.velocity_filter_weight) * this._lastVelocity;
45238 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45241 start: this._lastWidth
45244 this._lastVelocity = velocity;
45245 this._lastWidth = newWidth;
45249 drawDot: function (_a) {
45250 var color = _a.color, point = _a.point;
45251 var ctx = this.canvasElCtx();
45252 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45254 this.drawCurveSegment(point.x, point.y, width);
45256 ctx.fillStyle = color;
45260 drawCurve: function (_a) {
45261 var color = _a.color, curve = _a.curve;
45262 var ctx = this.canvasElCtx();
45263 var widthDelta = curve.endWidth - curve.startWidth;
45264 var drawSteps = Math.floor(curve.length()) * 2;
45266 ctx.fillStyle = color;
45267 for (var i = 0; i < drawSteps; i += 1) {
45268 var t = i / drawSteps;
45274 var x = uuu * curve.startPoint.x;
45275 x += 3 * uu * t * curve.control1.x;
45276 x += 3 * u * tt * curve.control2.x;
45277 x += ttt * curve.endPoint.x;
45278 var y = uuu * curve.startPoint.y;
45279 y += 3 * uu * t * curve.control1.y;
45280 y += 3 * u * tt * curve.control2.y;
45281 y += ttt * curve.endPoint.y;
45282 var width = curve.startWidth + ttt * widthDelta;
45283 this.drawCurveSegment(x, y, width);
45289 drawCurveSegment: function (x, y, width) {
45290 var ctx = this.canvasElCtx();
45292 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45293 this.is_empty = false;
45298 var ctx = this.canvasElCtx();
45299 var canvas = this.canvasEl().dom;
45300 ctx.fillStyle = this.bg_color;
45301 ctx.clearRect(0, 0, canvas.width, canvas.height);
45302 ctx.fillRect(0, 0, canvas.width, canvas.height);
45303 this.curve_data = [];
45305 this.is_empty = true;
45310 return this.el.select('input',true).first();
45313 canvasEl: function()
45315 return this.el.select('canvas',true).first();
45318 canvasElCtx: function()
45320 return this.el.select('canvas',true).first().dom.getContext('2d');
45323 getImage: function(type)
45325 if(this.is_empty) {
45330 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45333 drawFromImage: function(img_src)
45335 var img = new Image();
45337 img.onload = function(){
45338 this.canvasElCtx().drawImage(img, 0, 0);
45343 this.is_empty = false;
45346 selectImage: function()
45348 this.fileEl().dom.click();
45351 uploadImage: function(e)
45353 var reader = new FileReader();
45355 reader.onload = function(e){
45356 var img = new Image();
45357 img.onload = function(){
45359 this.canvasElCtx().drawImage(img, 0, 0);
45361 img.src = e.target.result;
45364 reader.readAsDataURL(e.target.files[0]);
45367 // Bezier Point Constructor
45368 Point: (function () {
45369 function Point(x, y, time) {
45372 this.time = time || Date.now();
45374 Point.prototype.distanceTo = function (start) {
45375 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45377 Point.prototype.equals = function (other) {
45378 return this.x === other.x && this.y === other.y && this.time === other.time;
45380 Point.prototype.velocityFrom = function (start) {
45381 return this.time !== start.time
45382 ? this.distanceTo(start) / (this.time - start.time)
45389 // Bezier Constructor
45390 Bezier: (function () {
45391 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45392 this.startPoint = startPoint;
45393 this.control2 = control2;
45394 this.control1 = control1;
45395 this.endPoint = endPoint;
45396 this.startWidth = startWidth;
45397 this.endWidth = endWidth;
45399 Bezier.fromPoints = function (points, widths, scope) {
45400 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45401 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45402 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45404 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45405 var dx1 = s1.x - s2.x;
45406 var dy1 = s1.y - s2.y;
45407 var dx2 = s2.x - s3.x;
45408 var dy2 = s2.y - s3.y;
45409 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45410 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45411 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45412 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45413 var dxm = m1.x - m2.x;
45414 var dym = m1.y - m2.y;
45415 var k = l2 / (l1 + l2);
45416 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45417 var tx = s2.x - cm.x;
45418 var ty = s2.y - cm.y;
45420 c1: new scope.Point(m1.x + tx, m1.y + ty),
45421 c2: new scope.Point(m2.x + tx, m2.y + ty)
45424 Bezier.prototype.length = function () {
45429 for (var i = 0; i <= steps; i += 1) {
45431 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45432 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45434 var xdiff = cx - px;
45435 var ydiff = cy - py;
45436 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45443 Bezier.prototype.point = function (t, start, c1, c2, end) {
45444 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45445 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45446 + (3.0 * c2 * (1.0 - t) * t * t)
45447 + (end * t * t * t);
45452 throttleStroke: function(fn, wait) {
45453 if (wait === void 0) { wait = 250; }
45455 var timeout = null;
45459 var later = function () {
45460 previous = Date.now();
45462 result = fn.apply(storedContext, storedArgs);
45464 storedContext = null;
45468 return function wrapper() {
45470 for (var _i = 0; _i < arguments.length; _i++) {
45471 args[_i] = arguments[_i];
45473 var now = Date.now();
45474 var remaining = wait - (now - previous);
45475 storedContext = this;
45477 if (remaining <= 0 || remaining > wait) {
45479 clearTimeout(timeout);
45483 result = fn.apply(storedContext, storedArgs);
45485 storedContext = null;
45489 else if (!timeout) {
45490 timeout = window.setTimeout(later, remaining);